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
;
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)))) {
62 if (returnType
!= null && returnType
== PsiType
.VOID
) return false;
64 else if (methodName
.startsWith("is")) {
65 return isBoolean(returnType
);
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) {
88 final PsiType returnType
= method
.getReturnType();
90 if (returnType
== null || returnType
== PsiType
.VOID
) {
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
);
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));
118 public static String
getPropertyNameBySetter(PsiMethod setterMethod
) {
119 String methodName
= setterMethod
.getName();
120 return Introspector
.decapitalize(methodName
.substring(3));
124 public static Map
<String
, PsiMethod
> getAllProperties(@NotNull final PsiClass psiClass
, final boolean acceptSetters
, final boolean acceptGetters
) {
125 return getAllProperties(psiClass
, acceptSetters
, acceptGetters
, true);
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
);
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;
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
)) {
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
)) {
182 @Nullable public static PsiMethod
findPropertyGetter(PsiClass aClass
,
185 boolean checkSuperClasses
) {
186 if (aClass
== null) return null;
188 if (checkSuperClasses
) {
189 methods
= aClass
.getAllMethods();
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
)) {
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
;
221 public static boolean isSimplePropertyAccessor(PsiMethod method
) {
222 return isSimplePropertyGetter(method
) || isSimplePropertySetter(method
);
225 @Nullable public static PsiMethod
findPropertySetter(PsiClass aClass
,
228 boolean checkSuperClasses
) {
229 if (aClass
== null) return null;
231 if (checkSuperClasses
) {
232 methods
= aClass
.getAllMethods();
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
)) {
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
;
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
;
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
;
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");
305 name
.insert(0, "get");
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());
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
>();
335 if (includeSuperClass
) {
336 methods
= aClass
.getAllMethods();
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
>();
358 if (includeSuperClass
) {
359 methods
= aClass
.getAllMethods();
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
);
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
);
395 catch (IncorrectOperationException e
) {
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
);
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
)) {
434 buffer
.append("this.");
437 String className
= containingClass
.getName();
438 if (className
!= null) {
439 buffer
.append(className
);
446 buffer
.append(parameterName
);
447 buffer
.append(";\n");
449 buffer
.append("return this;\n");
452 PsiCodeBlock body
= factory
.createCodeBlockFromText(buffer
.toString(), null);
453 setMethod
.getBody().replace(body
);
454 setMethod
= (PsiMethod
)CodeStyleManager
.getInstance(project
).reformat(setMethod
);
457 catch (IncorrectOperationException e
) {
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
);
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();
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();
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();
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();
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;
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
;