allow "is" getters java.lang.Boolean. Against spec, but allowed every-other-where.
[fedora-idea.git] / openapi / src / com / intellij / psi / util / PropertyUtil.java
blob2da284e202d92989281d5cc88e18e7a6ff43314e
1 /*
2 * Copyright 2000-2007 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.psi.util;
18 import com.intellij.codeInsight.AnnotationUtil;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.util.Comparing;
22 import com.intellij.openapi.util.text.StringUtil;
23 import com.intellij.psi.*;
24 import com.intellij.psi.codeStyle.CodeStyleManager;
25 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
26 import com.intellij.psi.codeStyle.VariableKind;
27 import com.intellij.util.ArrayUtil;
28 import com.intellij.util.IncorrectOperationException;
29 import org.jetbrains.annotations.NonNls;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
33 import java.beans.Introspector;
34 import java.util.*;
36 /**
37 * @author Mike
39 public class PropertyUtil {
40 private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.PropertyUtil");
42 private PropertyUtil() {
45 public static boolean isSimplePropertyGetter(PsiMethod method) {
46 return hasGetterName(method) && method.getParameterList().getParametersCount() == 0;
49 @SuppressWarnings({"HardCodedStringLiteral"})
50 public static boolean hasGetterName(final PsiMethod method) {
51 if (method == null) return false;
53 if (method.isConstructor()) return false;
55 String methodName = method.getName();
56 PsiType returnType = method.getReturnType();
57 if (methodName.startsWith("get") && methodName.length() > "get".length()) {
58 if (Character.isLowerCase(methodName.charAt("get".length()))
59 && (methodName.length() == "get".length() + 1 || Character.isLowerCase(methodName.charAt("get".length() + 1)))) {
60 return false;
62 if (returnType != null && returnType == PsiType.VOID) return false;
64 else if (methodName.startsWith("is")) {
65 return isBoolean(returnType);
67 else {
68 return false;
70 return true;
73 @SuppressWarnings("HardCodedStringLiteral")
74 public static boolean isSimplePropertySetter(PsiMethod method) {
75 if (method == null) return false;
77 if (method.isConstructor()) return false;
79 String methodName = method.getName();
81 if (!(methodName.startsWith("set") && methodName.length() > "set".length())) return false;
82 if (Character.isLowerCase(methodName.charAt("set".length()))) return false;
84 if (method.getParameterList().getParametersCount() != 1) {
85 return false;
88 final PsiType returnType = method.getReturnType();
90 if (returnType == null || returnType == PsiType.VOID) {
91 return true;
94 return Comparing.equal(PsiUtil.resolveClassInType(returnType), method.getContainingClass());
97 @Nullable public static String getPropertyName(PsiMethod method) {
98 if (isSimplePropertyGetter(method)) {
99 return getPropertyNameByGetter(method);
101 else if (isSimplePropertySetter(method)) {
102 return getPropertyNameBySetter(method);
104 else {
105 return null;
109 @NotNull
110 public static String getPropertyNameByGetter(PsiMethod getterMethod) {
111 @NonNls String methodName = getterMethod.getName();
112 return methodName.startsWith("get") ?
113 StringUtil.decapitalize(methodName.substring(3)) :
114 StringUtil.decapitalize(methodName.substring(2));
117 @NotNull
118 public static String getPropertyNameBySetter(PsiMethod setterMethod) {
119 String methodName = setterMethod.getName();
120 return Introspector.decapitalize(methodName.substring(3));
123 @NotNull
124 public static Map<String, PsiMethod> getAllProperties(@NotNull final PsiClass psiClass, final boolean acceptSetters, final boolean acceptGetters) {
125 return getAllProperties(psiClass, acceptSetters, acceptGetters, true);
128 @NotNull
129 public static Map<String, PsiMethod> getAllProperties(@NotNull final PsiClass psiClass, final boolean acceptSetters, final boolean acceptGetters, final boolean includeSuperClass) {
130 final Map<String, PsiMethod> map = new HashMap<String, PsiMethod>();
131 final PsiMethod[] methods = includeSuperClass ? psiClass.getAllMethods() : psiClass.getMethods();
133 for (PsiMethod method : methods) {
134 if (filterMethods(method)) continue;
135 if (acceptSetters && PropertyUtil.isSimplePropertySetter(method)||
136 acceptGetters && PropertyUtil.isSimplePropertyGetter(method)) {
137 map.put(PropertyUtil.getPropertyName(method), method);
140 return map;
144 private static boolean filterMethods(final PsiMethod method) {
145 if(method.hasModifierProperty(PsiModifier.STATIC) || !method.hasModifierProperty(PsiModifier.PUBLIC)) return true;
147 final String className = method.getContainingClass().getQualifiedName();
148 if (className != null && className.equals(CommonClassNames.JAVA_LANG_OBJECT)) return true;
149 return false;
152 @NotNull
153 public static List<PsiMethod> getSetters(@NotNull final PsiClass psiClass, final String propertyName) {
154 final String setterName = suggestSetterName(propertyName);
155 final PsiMethod[] psiMethods = psiClass.findMethodsByName(setterName, true);
156 final ArrayList<PsiMethod> list = new ArrayList<PsiMethod>(psiMethods.length);
157 for (PsiMethod method : psiMethods) {
158 if (filterMethods(method)) continue;
159 if (PropertyUtil.isSimplePropertySetter(method)) {
160 list.add(method);
163 return list;
166 @NotNull
167 public static List<PsiMethod> getGetters(@NotNull final PsiClass psiClass, final String propertyName) {
168 final String[] names = suggestGetterNames(propertyName);
169 final ArrayList<PsiMethod> list = new ArrayList<PsiMethod>();
170 for (String name : names) {
171 final PsiMethod[] psiMethods = psiClass.findMethodsByName(name, true);
172 for (PsiMethod method : psiMethods) {
173 if (filterMethods(method)) continue;
174 if (PropertyUtil.isSimplePropertyGetter(method)) {
175 list.add(method);
179 return list;
182 @Nullable public static PsiMethod findPropertyGetter(PsiClass aClass,
183 String propertyName,
184 boolean isStatic,
185 boolean checkSuperClasses) {
186 if (aClass == null) return null;
187 PsiMethod[] methods;
188 if (checkSuperClasses) {
189 methods = aClass.getAllMethods();
191 else {
192 methods = aClass.getMethods();
195 for (PsiMethod method : methods) {
196 if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
198 if (isSimplePropertyGetter(method)) {
199 if (getPropertyNameByGetter(method).equals(propertyName)) {
200 return method;
205 return null;
208 @Nullable public static PsiMethod findPropertyGetterWithType(String propertyName, boolean isStatic, PsiType type, Iterator<PsiMethod> methods) {
209 while (methods.hasNext()) {
210 PsiMethod method = methods.next();
211 if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
212 if (isSimplePropertyGetter(method)) {
213 if (getPropertyNameByGetter(method).equals(propertyName)) {
214 if (type.equals(method.getReturnType())) return method;
218 return null;
221 public static boolean isSimplePropertyAccessor(PsiMethod method) {
222 return isSimplePropertyGetter(method) || isSimplePropertySetter(method);
225 @Nullable public static PsiMethod findPropertySetter(PsiClass aClass,
226 String propertyName,
227 boolean isStatic,
228 boolean checkSuperClasses) {
229 if (aClass == null) return null;
230 PsiMethod[] methods;
231 if (checkSuperClasses) {
232 methods = aClass.getAllMethods();
234 else {
235 methods = aClass.getMethods();
238 for (PsiMethod method : methods) {
239 if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
241 if (isSimplePropertySetter(method)) {
242 if (getPropertyNameBySetter(method).equals(propertyName)) {
243 return method;
248 return null;
251 @Nullable public static PsiMethod findPropertySetterWithType(String propertyName, boolean isStatic, PsiType type, Iterator<PsiMethod> methods) {
252 while (methods.hasNext()) {
253 PsiMethod method = methods.next();
254 if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
256 if (isSimplePropertySetter(method)) {
257 if (getPropertyNameBySetter(method).equals(propertyName)) {
258 PsiType methodType = method.getParameterList().getParameters()[0].getType();
259 if (type.equals(methodType)) return method;
263 return null;
266 @Nullable public static PsiField findPropertyField(Project project, PsiClass aClass, String propertyName, boolean isStatic) {
267 PsiField[] fields = aClass.getAllFields();
269 for (PsiField field : fields) {
270 if (field.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
271 if (propertyName.equals(suggestPropertyName(project, field))) return field;
274 return null;
277 @Nullable public static PsiField findPropertyFieldWithType(Project project, String propertyName,
278 boolean isStatic, PsiType type, Iterator<PsiField> fields) {
279 while (fields.hasNext()) {
280 PsiField field = fields.next();
281 if (field.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue;
282 if (propertyName.equals(suggestPropertyName(project, field))) {
283 if (type.equals(field.getType())) return field;
287 return null;
290 @Nullable public static String getPropertyName(@NonNls String methodName) {
291 return StringUtil.getPropertyName(methodName);
294 public static String suggestGetterName(@NotNull String propertyName, @Nullable PsiType propertyType) {
295 return suggestGetterName(propertyName, propertyType, null);
298 public static String suggestGetterName(@NotNull String propertyName, @Nullable PsiType propertyType, @NonNls String existingGetterName) {
299 @NonNls StringBuffer name = new StringBuffer(StringUtil.capitalize(propertyName));
300 if (isBoolean(propertyType)) {
301 if (existingGetterName == null || !existingGetterName.startsWith("get")) {
302 name.insert(0, "is");
304 else {
305 name.insert(0, "get");
308 else {
309 name.insert(0, "get");
312 return name.toString();
315 private static boolean isBoolean(@Nullable PsiType propertyType) {
316 return propertyType == PsiType.BOOLEAN || propertyType != null && CommonClassNames.JAVA_LANG_BOOLEAN.equals(propertyType.getCanonicalText());
319 @NonNls
320 public static String[] suggestGetterNames(String propertyName) {
321 final String str = StringUtil.capitalize(propertyName);
322 return new String[] { "is" + str, "get" + str };
325 public static String suggestSetterName(String propertyName) {
326 @NonNls StringBuffer name = new StringBuffer(StringUtil.capitalize(propertyName));
327 name.insert(0, "set");
328 return name.toString();
331 public static String[] getReadableProperties(PsiClass aClass, boolean includeSuperClass) {
332 List<String> result = new ArrayList<String>();
334 PsiMethod[] methods;
335 if (includeSuperClass) {
336 methods = aClass.getAllMethods();
338 else {
339 methods = aClass.getMethods();
342 for (PsiMethod method : methods) {
343 if ("java.lang.Object".equals(method.getContainingClass().getQualifiedName())) continue;
345 if (isSimplePropertyGetter(method)) {
346 result.add(getPropertyName(method));
350 return ArrayUtil.toStringArray(result);
353 public static String[] getWritableProperties(PsiClass aClass, boolean includeSuperClass) {
354 List<String> result = new ArrayList<String>();
356 PsiMethod[] methods;
358 if (includeSuperClass) {
359 methods = aClass.getAllMethods();
361 else {
362 methods = aClass.getMethods();
365 for (PsiMethod method : methods) {
366 if ("java.lang.Object".equals(method.getContainingClass().getQualifiedName())) continue;
368 if (isSimplePropertySetter(method)) {
369 result.add(getPropertyName(method));
373 return ArrayUtil.toStringArray(result);
376 public static PsiMethod generateGetterPrototype(PsiField field) {
377 PsiElementFactory factory = JavaPsiFacade.getInstance(field.getProject()).getElementFactory();
378 Project project = field.getProject();
379 String name = field.getName();
380 String getName = suggestGetterName(project, field);
381 try {
382 PsiMethod getMethod = factory.createMethod(getName, field.getType());
383 PsiUtil.setModifierProperty(getMethod, PsiModifier.PUBLIC, true);
384 if (field.hasModifierProperty(PsiModifier.STATIC)) {
385 PsiUtil.setModifierProperty(getMethod, PsiModifier.STATIC, true);
388 annotateWithNullableStuff(field, factory, getMethod);
390 PsiCodeBlock body = factory.createCodeBlockFromText("{\nreturn " + name + ";\n}", null);
391 getMethod.getBody().replace(body);
392 getMethod = (PsiMethod)CodeStyleManager.getInstance(project).reformat(getMethod);
393 return getMethod;
395 catch (IncorrectOperationException e) {
396 LOG.error(e);
397 return null;
401 public static PsiMethod generateSetterPrototype(PsiField field) {
402 return generateSetterPrototype(field, field.getContainingClass());
405 public static PsiMethod generateSetterPrototype(PsiField field, final PsiClass containingClass) {
406 return generateSetterPrototype(field, containingClass, false);
409 public static PsiMethod generateSetterPrototype(PsiField field, final PsiClass containingClass, boolean returnSelf) {
410 Project project = field.getProject();
411 JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
412 PsiElementFactory factory = JavaPsiFacade.getInstance(field.getProject()).getElementFactory();
414 String name = field.getName();
415 boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
416 VariableKind kind = codeStyleManager.getVariableKind(field);
417 String propertyName = codeStyleManager.variableNameToPropertyName(name, kind);
418 String setName = suggestSetterName(project, field);
419 try {
420 PsiMethod setMethod = factory.createMethod(setName, returnSelf ? factory.createType(containingClass) : PsiType.VOID);
421 String parameterName = codeStyleManager.propertyNameToVariableName(propertyName, VariableKind.PARAMETER);
422 PsiParameter param = factory.createParameter(parameterName, field.getType());
424 annotateWithNullableStuff(field, factory, param);
426 setMethod.getParameterList().add(param);
427 PsiUtil.setModifierProperty(setMethod, PsiModifier.PUBLIC, true);
428 PsiUtil.setModifierProperty(setMethod, PsiModifier.STATIC, isStatic);
430 @NonNls StringBuffer buffer = new StringBuffer();
431 buffer.append("{\n");
432 if (name.equals(parameterName)) {
433 if (!isStatic) {
434 buffer.append("this.");
436 else {
437 String className = containingClass.getName();
438 if (className != null) {
439 buffer.append(className);
440 buffer.append(".");
444 buffer.append(name);
445 buffer.append("=");
446 buffer.append(parameterName);
447 buffer.append(";\n");
448 if (returnSelf) {
449 buffer.append("return this;\n");
451 buffer.append("}");
452 PsiCodeBlock body = factory.createCodeBlockFromText(buffer.toString(), null);
453 setMethod.getBody().replace(body);
454 setMethod = (PsiMethod)CodeStyleManager.getInstance(project).reformat(setMethod);
455 return setMethod;
457 catch (IncorrectOperationException e) {
458 LOG.error(e);
459 return null;
463 private static void annotateWithNullableStuff(final PsiModifierListOwner field, final PsiElementFactory factory, final PsiModifierListOwner listOwner)
464 throws IncorrectOperationException {
465 if (AnnotationUtil.isAnnotated(field, AnnotationUtil.NOT_NULL, false)) {
466 annotate(factory, listOwner, AnnotationUtil.NOT_NULL);
468 else if (AnnotationUtil.isAnnotated(field, AnnotationUtil.NULLABLE, false)) {
469 annotate(factory, listOwner, AnnotationUtil.NULLABLE);
473 private static void annotate(final PsiElementFactory factory, final PsiModifierListOwner listOwner, final String annotationQName)
474 throws IncorrectOperationException {
475 final PsiModifierList modifierList = listOwner.getModifierList();
476 LOG.assertTrue(modifierList != null);
477 modifierList.addAfter(factory.createAnnotationFromText("@" + annotationQName, listOwner), null);
480 public static String suggestPropertyName(Project project, PsiField field) {
481 JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
482 VariableKind kind = codeStyleManager.getVariableKind(field);
483 return codeStyleManager.variableNameToPropertyName(field.getName(), kind);
486 public static String suggestGetterName(Project project, PsiField field) {
487 String propertyName = suggestPropertyName(project, field);
488 return suggestGetterName(propertyName, field.getType());
491 public static String suggestSetterName(Project project, PsiField field) {
492 String propertyName = suggestPropertyName(project, field);
493 return suggestSetterName(propertyName);
497 * "xxx", "void setMyProperty(String pp)" -> "setXxx"
499 @Nullable public static String suggestPropertyAccessor(String name, PsiMethod accessorTemplate) {
500 if (isSimplePropertyGetter(accessorTemplate)) {
501 PsiType type = accessorTemplate.getReturnType();
502 return suggestGetterName(name, type, accessorTemplate.getName());
504 if (isSimplePropertySetter(accessorTemplate)) {
505 return suggestSetterName(name);
507 return null;
510 @Nullable
511 public static String getPropertyName(final PsiMember member) {
512 if (member instanceof PsiMethod) {
513 return getPropertyName((PsiMethod)member);
515 else if (member instanceof PsiField) {
516 return member.getName();
518 else return null;
521 @Nullable
522 public static PsiType getPropertyType(final PsiMember member) {
523 if (member instanceof PsiField) {
524 return ((PsiField)member).getType();
526 else if (member instanceof PsiMethod) {
527 final PsiMethod psiMethod = (PsiMethod)member;
528 if (isSimplePropertyGetter(psiMethod)) {
529 return psiMethod.getReturnType();
531 else if (isSimplePropertySetter(psiMethod)) {
532 return psiMethod.getParameterList().getParameters()[0].getType();
535 return null;
538 @Nullable
539 public static PsiTypeElement getPropertyTypeElement(final PsiMember member) {
540 if (member instanceof PsiField) {
541 return ((PsiField)member).getTypeElement();
543 else if (member instanceof PsiMethod) {
544 final PsiMethod psiMethod = (PsiMethod)member;
545 if (isSimplePropertyGetter(psiMethod)) {
546 return psiMethod.getReturnTypeElement();
548 else if (isSimplePropertySetter(psiMethod)) {
549 return psiMethod.getParameterList().getParameters()[0].getTypeElement();
552 return null;
555 @Nullable
556 public static PsiIdentifier getPropertyNameIdentifier(final PsiMember member) {
557 if (member instanceof PsiField) {
558 return ((PsiField)member).getNameIdentifier();
560 else if (member instanceof PsiMethod) {
561 return ((PsiMethod)member).getNameIdentifier();
563 return null;
566 @Nullable
567 public static PsiField findPropertyFieldByMember(final PsiMember psiMember) {
568 if (psiMember instanceof PsiField) {
569 return (PsiField)psiMember;
571 else if (psiMember instanceof PsiMethod) {
572 final PsiMethod psiMethod = (PsiMethod)psiMember;
573 final PsiType returnType = psiMethod.getReturnType();
574 if (returnType == null) return null;
575 final PsiCodeBlock body = psiMethod.getBody();
576 final PsiStatement[] statements = body == null? null : body.getStatements();
577 final PsiStatement statement = statements == null || statements.length != 1? null : statements[0];
578 final PsiElement target;
579 if (returnType == PsiType.VOID) {
580 final PsiExpression expression = statement instanceof PsiExpressionStatement? ((PsiExpressionStatement)statement).getExpression() : null;
581 target = expression instanceof PsiAssignmentExpression? ((PsiAssignmentExpression)expression).getLExpression() : null;
583 else {
584 target = statement instanceof PsiReturnStatement ? ((PsiReturnStatement)statement).getReturnValue() : null;
586 final PsiElement resolved = target instanceof PsiReferenceExpression ? ((PsiReferenceExpression)target).resolve() : null;
587 if (resolved instanceof PsiField) {
588 final PsiField field = (PsiField)resolved;
589 if (psiMember.getContainingClass() == field.getContainingClass() ||
590 psiMember.getContainingClass().isInheritor(field.getContainingClass(), true)) return field;
593 return null;