| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.jetbrains.plugins.groovy.lang.psi.util; |
| |
| import com.intellij.codeInsight.NullableNotNullManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.JavaCodeStyleManager; |
| import com.intellij.psi.codeStyle.VariableKind; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; |
| import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; |
| import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; |
| import org.jetbrains.plugins.groovy.lang.resolve.processors.AccessorResolverProcessor; |
| |
| import java.beans.Introspector; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * @author ilyas |
| */ |
| public class GroovyPropertyUtils { |
| private static final Logger LOG = Logger.getInstance(GroovyPropertyUtils.class); |
| |
| public static final String IS_PREFIX = "is"; |
| public static final String GET_PREFIX = "get"; |
| public static final String SET_PREFIX = "set"; |
| |
| private GroovyPropertyUtils() { |
| } |
| |
| public static PsiMethod[] getAllSettersByField(PsiField field) { |
| return getAllSetters(field.getContainingClass(), field.getName(), field.hasModifierProperty(PsiModifier.STATIC), false); |
| } |
| |
| @NotNull |
| public static PsiMethod[] getAllGettersByField(PsiField field) { |
| return getAllGetters(field.getContainingClass(), field.getName(), field.hasModifierProperty(PsiModifier.STATIC), false); |
| } |
| |
| @Nullable |
| public static PsiMethod findSetterForField(PsiField field) { |
| final PsiClass containingClass = field.getContainingClass(); |
| final String propertyName = field.getName(); |
| final boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC); |
| return findPropertySetter(containingClass, propertyName, isStatic, true); |
| } |
| |
| @Nullable |
| public static PsiMethod findGetterForField(PsiField field) { |
| final PsiClass containingClass = field.getContainingClass(); |
| final String propertyName = field.getName(); |
| final boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC); |
| return findPropertyGetter(containingClass, propertyName, isStatic, true); |
| } |
| |
| @Nullable |
| public static PsiMethod findPropertySetter(@Nullable PsiType type, String propertyName, @NotNull GroovyPsiElement context) { |
| final String setterName = getSetterName(propertyName); |
| if (type == null) { |
| final GrExpression fromText = GroovyPsiElementFactory.getInstance(context.getProject()).createExpressionFromText("this", context); |
| return findPropertySetter(fromText.getType(), propertyName, context); |
| } |
| |
| final AccessorResolverProcessor processor = new AccessorResolverProcessor(setterName, propertyName, context, false); |
| ResolveUtil.processAllDeclarations(type, processor, ResolveState.initial(), context); |
| final GroovyResolveResult[] setterCandidates = processor.getCandidates(); |
| return PsiImplUtil.extractUniqueElement(setterCandidates); |
| } |
| |
| @Nullable |
| public static PsiMethod findPropertySetter(PsiClass aClass, String propertyName, boolean isStatic, boolean checkSuperClasses) { |
| if (aClass == null) return null; |
| PsiMethod[] methods; |
| if (checkSuperClasses) { |
| methods = aClass.getAllMethods(); |
| } |
| else { |
| methods = aClass.getMethods(); |
| } |
| |
| for (PsiMethod method : methods) { |
| if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue; |
| |
| if (isSimplePropertySetter(method)) { |
| if (propertyName.equals(getPropertyNameBySetter(method))) { |
| return method; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| @NotNull |
| public static PsiMethod[] getAllGetters(PsiClass aClass, @NotNull String propertyName, boolean isStatic, boolean checkSuperClasses) { |
| if (aClass == null) return PsiMethod.EMPTY_ARRAY; |
| PsiMethod[] methods; |
| if (checkSuperClasses) { |
| methods = aClass.getAllMethods(); |
| } |
| else { |
| methods = aClass.getMethods(); |
| } |
| |
| List<PsiMethod> result = new ArrayList<PsiMethod>(); |
| for (PsiMethod method : methods) { |
| if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue; |
| |
| if (isSimplePropertyGetter(method)) { |
| if (propertyName.equals(getPropertyNameByGetter(method))) { |
| result.add(method); |
| } |
| } |
| } |
| |
| return result.toArray(new PsiMethod[result.size()]); |
| } |
| |
| @NotNull |
| public static PsiMethod[] getAllSetters(PsiClass aClass, @NotNull String propertyName, boolean isStatic, boolean checkSuperClasses) { |
| if (aClass == null) return PsiMethod.EMPTY_ARRAY; |
| PsiMethod[] methods; |
| if (checkSuperClasses) { |
| methods = aClass.getAllMethods(); |
| } |
| else { |
| methods = aClass.getMethods(); |
| } |
| |
| List<PsiMethod> result = new ArrayList<PsiMethod>(); |
| for (PsiMethod method : methods) { |
| if (method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue; |
| |
| if (isSimplePropertySetter(method)) { |
| if (propertyName.equals(getPropertyNameBySetter(method))) { |
| result.add(method); |
| } |
| } |
| } |
| |
| return result.toArray(new PsiMethod[result.size()]); |
| } |
| |
| |
| @Nullable |
| public static PsiMethod findPropertyGetter(@Nullable PsiClass aClass, |
| String propertyName, |
| @Nullable Boolean isStatic, |
| boolean checkSuperClasses) { |
| if (aClass == null) return null; |
| PsiMethod[] methods; |
| if (checkSuperClasses) { |
| methods = aClass.getAllMethods(); |
| } |
| else { |
| methods = aClass.getMethods(); |
| } |
| |
| for (PsiMethod method : methods) { |
| if (isStatic != null && method.hasModifierProperty(PsiModifier.STATIC) != isStatic) continue; |
| |
| if (isSimplePropertyGetter(method)) { |
| if (propertyName.equals(getPropertyNameByGetter(method))) { |
| return method; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| public static boolean isSimplePropertyAccessor(PsiMethod method) { |
| return isSimplePropertyGetter(method) || isSimplePropertySetter(method); |
| }//do not check return type |
| |
| public static boolean isSimplePropertyGetter(PsiMethod method) { |
| return isSimplePropertyGetter(method, null); |
| }//do not check return type |
| |
| public static boolean isSimplePropertyGetter(PsiMethod method, @Nullable String propertyName) { |
| if (method == null || method.isConstructor()) return false; |
| if (method.getParameterList().getParametersCount() != 0) return false; |
| if (!isGetterName(method.getName())) return false; |
| if (method.getName().startsWith(IS_PREFIX) && !PsiType.BOOLEAN.equals(method.getReturnType())) { |
| return false; |
| } |
| if (method.getReturnType() == PsiType.VOID) return false; |
| if (propertyName == null) return true; |
| |
| final String byGetter = getPropertyNameByGetter(method); |
| return propertyName.equals(byGetter) || (!isPropertyName(byGetter) && propertyName.equals( |
| getPropertyNameByGetterName(method.getName(), PsiType.BOOLEAN.equals(method.getReturnType())))); |
| } |
| |
| public static boolean isSimplePropertySetter(PsiMethod method) { |
| return isSimplePropertySetter(method, null); |
| } |
| |
| public static boolean isSimplePropertySetter(PsiMethod method, @Nullable String propertyName) { |
| if (method == null || method.isConstructor()) return false; |
| if (method.getParameterList().getParametersCount() != 1) return false; |
| if (!isSetterName(method.getName())) return false; |
| if (propertyName==null) return true; |
| |
| final String bySetter = getPropertyNameBySetter(method); |
| return propertyName.equals(bySetter) || (!isPropertyName(bySetter) && propertyName.equals(getPropertyNameBySetterName(method.getName()))); |
| } |
| |
| @Nullable |
| public static String getPropertyNameByGetter(PsiMethod getterMethod) { |
| if (getterMethod instanceof GrAccessorMethod) { |
| return ((GrAccessorMethod)getterMethod).getProperty().getName(); |
| } |
| |
| @NonNls String methodName = getterMethod.getName(); |
| final boolean isPropertyBoolean = PsiType.BOOLEAN.equals(getterMethod.getReturnType()); |
| return getPropertyNameByGetterName(methodName, isPropertyBoolean); |
| } |
| |
| @Nullable |
| public static String getPropertyNameByGetterName(@NotNull String methodName, boolean canBeBoolean) { |
| if (methodName.startsWith(GET_PREFIX) && methodName.length() > 3) { |
| return decapitalize(methodName.substring(3)); |
| } |
| if (canBeBoolean && methodName.startsWith(IS_PREFIX) && methodName.length() > 2) { |
| return decapitalize(methodName.substring(2)); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static String getPropertyNameBySetter(PsiMethod setterMethod) { |
| if (setterMethod instanceof GrAccessorMethod) { |
| return ((GrAccessorMethod)setterMethod).getProperty().getName(); |
| } |
| |
| @NonNls String methodName = setterMethod.getName(); |
| return getPropertyNameBySetterName(methodName); |
| } |
| |
| @Nullable |
| public static String getPropertyNameBySetterName(@NotNull String methodName) { |
| if (methodName.startsWith(SET_PREFIX) && methodName.length() > 3) { |
| return StringUtil.decapitalize(methodName.substring(3)); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| @Nullable |
| public static String getPropertyNameByAccessorName(String accessorName) { |
| if (isGetterName(accessorName)) { |
| return getPropertyNameByGetterName(accessorName, true); |
| } |
| else if (isSetterName(accessorName)) { |
| return getPropertyNameBySetterName(accessorName); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static String getPropertyName(PsiMethod accessor) { |
| if (isSimplePropertyGetter(accessor)) return getPropertyNameByGetter(accessor); |
| if (isSimplePropertySetter(accessor)) return getPropertyNameBySetter(accessor); |
| return null; |
| } |
| |
| public static boolean isGetterName(@NotNull String name) { |
| int prefixLength; |
| if (name.startsWith(GET_PREFIX)) { |
| prefixLength = 3; |
| } |
| else if (name.startsWith(IS_PREFIX)) { |
| prefixLength = 2; |
| } |
| else { |
| return false; |
| } |
| |
| if (name.length() == prefixLength) return false; |
| |
| if (isUpperCase(name.charAt(prefixLength))) return true; |
| |
| return name.length() > prefixLength + 1 && isUpperCase(name.charAt(prefixLength + 1)); |
| } |
| |
| public static String getGetterNameNonBoolean(@NotNull String name) { |
| return getAccessorName(GET_PREFIX, name); |
| } |
| |
| public static String getGetterNameBoolean(@NotNull String name) { |
| return getAccessorName(IS_PREFIX, name); |
| } |
| |
| public static String getSetterName(@NotNull String name) { |
| return getAccessorName("set", name); |
| } |
| |
| public static String getAccessorName(String prefix, String name) { |
| if (name.isEmpty()) return prefix; |
| |
| StringBuilder sb = new StringBuilder(); |
| sb.append(prefix); |
| if (name.length() > 1 && Character.isUpperCase(name.charAt(1))) { |
| sb.append(name); |
| } |
| else { |
| sb.append(Character.toUpperCase(name.charAt(0))); |
| sb.append(name, 1, name.length()); |
| } |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Returns getter names in priority order |
| * @param name property name |
| * @return getter names |
| */ |
| public static String[] suggestGettersName(@NotNull String name) { |
| return new String[]{getGetterNameBoolean(name), getGetterNameNonBoolean(name)}; |
| } |
| |
| public static boolean isPropertyName(String name) { |
| if (name.isEmpty()) return false; |
| if (Character.isUpperCase(name.charAt(0)) && (name.length() == 1 || !Character.isUpperCase(name.charAt(1)))) return false; |
| return true; |
| } |
| |
| public static String[] suggestSettersName(@NotNull String name) { |
| return new String[]{getSetterName(name)}; |
| } |
| |
| public static boolean isSetterName(String name) { |
| return name != null |
| && name.startsWith(SET_PREFIX) |
| && name.length() > 3 |
| && (isUpperCase(name.charAt(3)) || (name.length() > 4 && isUpperCase(name.charAt(3)))); |
| } |
| |
| public static boolean isProperty(@Nullable PsiClass aClass, @Nullable String propertyName, boolean isStatic) { |
| if (aClass == null || propertyName == null) return false; |
| final PsiField field = aClass.findFieldByName(propertyName, true); |
| if (field instanceof GrField && ((GrField)field).isProperty() && field.hasModifierProperty(PsiModifier.STATIC) == isStatic) return true; |
| |
| final PsiMethod getter = findPropertyGetter(aClass, propertyName, isStatic, true); |
| if (getter != null && getter.hasModifierProperty(PsiModifier.PUBLIC)) return true; |
| |
| final PsiMethod setter = findPropertySetter(aClass, propertyName, isStatic, true); |
| return setter != null && setter.hasModifierProperty(PsiModifier.PUBLIC); |
| } |
| |
| public static boolean isProperty(GrField field) { |
| final PsiClass clazz = field.getContainingClass(); |
| return isProperty(clazz, field.getName(), field.hasModifierProperty(PsiModifier.STATIC)); |
| } |
| |
| private static boolean isUpperCase(char c) { |
| return Character.toUpperCase(c) == c; |
| } |
| |
| /*public static boolean canBePropertyName(String name) { |
| return !(name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isLowerCase(name.charAt(0))); |
| }*/ |
| |
| public static String capitalize(String s) { |
| if (s.isEmpty()) return s; |
| if (s.length() == 1) return s.toUpperCase(); |
| if (Character.isUpperCase(s.charAt(1))) return s; |
| final char[] chars = s.toCharArray(); |
| chars[0] = Character.toUpperCase(chars[0]); |
| return new String(chars); |
| } |
| |
| public static String decapitalize(String s) { |
| return Introspector.decapitalize(s); |
| } |
| |
| @Nullable |
| public static PsiField findFieldForAccessor(PsiMethod accessor, boolean checkSuperClasses) { |
| final PsiClass psiClass = accessor.getContainingClass(); |
| if (psiClass == null) return null; |
| PsiField field = null; |
| if (!checkSuperClasses) { |
| field = psiClass.findFieldByName(getPropertyNameByAccessorName(accessor.getName()), true); |
| } |
| else { |
| final String name = getPropertyNameByAccessorName(accessor.getName()); |
| assert name != null; |
| final PsiField[] allFields = psiClass.getAllFields(); |
| for (PsiField psiField : allFields) { |
| if (name.equals(psiField.getName())) { |
| field = psiField; |
| break; |
| } |
| } |
| } |
| if (field == null) return null; |
| if (field.hasModifierProperty(PsiModifier.STATIC) == accessor.hasModifierProperty(PsiModifier.STATIC)) { |
| return field; |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static String getGetterPrefix(PsiMethod getter) { |
| final String name = getter.getName(); |
| if (name.startsWith(GET_PREFIX)) return GET_PREFIX; |
| if (name.startsWith(IS_PREFIX)) return IS_PREFIX; |
| |
| return null; |
| } |
| |
| @Nullable |
| public static String getSetterPrefix(PsiMethod setter) { |
| if (setter.getName().startsWith(SET_PREFIX)) return SET_PREFIX; |
| return null; |
| } |
| |
| @Nullable |
| public static String getAccessorPrefix(PsiMethod method) { |
| final String prefix = getGetterPrefix(method); |
| if (prefix != null) return prefix; |
| |
| return getSetterPrefix(method); |
| } |
| |
| public static boolean isAccessorFor(PsiMethod accessor, PsiField field) { |
| final String accessorName = accessor.getName(); |
| final String fieldName = field.getName(); |
| if (!ArrayUtil.contains(accessorName, suggestGettersName(fieldName)) && |
| !ArrayUtil.contains(accessorName, suggestSettersName(fieldName))) { |
| return false; |
| } |
| final PsiClass accessorClass = accessor.getContainingClass(); |
| final PsiClass fieldClass = field.getContainingClass(); |
| if (!field.getManager().areElementsEquivalent(accessorClass, fieldClass)) return false; |
| return accessor.hasModifierProperty(PsiModifier.STATIC) == field.hasModifierProperty(PsiModifier.STATIC); |
| } |
| |
| public static List<GrAccessorMethod> getFieldAccessors(GrField field) { |
| List<GrAccessorMethod> accessors = new ArrayList<GrAccessorMethod>(); |
| final GrAccessorMethod[] getters = field.getGetters(); |
| Collections.addAll(accessors, getters); |
| final GrAccessorMethod setter = field.getSetter(); |
| if (setter != null) accessors.add(setter); |
| return accessors; |
| } |
| |
| public static GrMethod generateGetterPrototype(PsiField field) { |
| GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(field.getProject()); |
| String name = field.getName(); |
| String getName = getGetterNameNonBoolean(field.getName()); |
| try { |
| PsiType type = field instanceof GrField ? ((GrField)field).getDeclaredType() : field.getType(); |
| GrMethod getter = factory.createMethod(getName, type); |
| if (field.hasModifierProperty(PsiModifier.STATIC)) { |
| PsiUtil.setModifierProperty(getter, PsiModifier.STATIC, true); |
| } |
| |
| annotateWithNullableStuff(field, getter); |
| |
| GrCodeBlock body = factory.createMethodBodyFromText("\nreturn " + name + "\n"); |
| getter.getBlock().replace(body); |
| return getter; |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| return null; |
| } |
| } |
| |
| public static GrMethod generateSetterPrototype(PsiField field) { |
| Project project = field.getProject(); |
| JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project); |
| GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(project); |
| |
| String name = field.getName(); |
| boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC); |
| VariableKind kind = codeStyleManager.getVariableKind(field); |
| String propertyName = codeStyleManager.variableNameToPropertyName(name, kind); |
| String setName = getSetterName(field.getName()); |
| |
| final PsiClass containingClass = field.getContainingClass(); |
| try { |
| GrMethod setMethod = factory.createMethod(setName, PsiType.VOID); |
| String parameterName = codeStyleManager.propertyNameToVariableName(propertyName, VariableKind.PARAMETER); |
| final PsiType type = field instanceof GrField ? ((GrField)field).getDeclaredType() : field.getType(); |
| GrParameter param = factory.createParameter(parameterName, type); |
| |
| annotateWithNullableStuff(field, param); |
| |
| setMethod.getParameterList().add(param); |
| PsiUtil.setModifierProperty(setMethod, PsiModifier.STATIC, isStatic); |
| |
| @NonNls StringBuilder builder = new StringBuilder(); |
| if (name.equals(parameterName)) { |
| if (!isStatic) { |
| builder.append("this."); |
| } |
| else { |
| String className = containingClass.getName(); |
| if (className != null) { |
| builder.append(className); |
| builder.append("."); |
| } |
| } |
| } |
| builder.append(name); |
| builder.append("="); |
| builder.append(parameterName); |
| builder.append("\n"); |
| GrCodeBlock body = factory.createMethodBodyFromText(builder.toString()); |
| setMethod.getBlock().replace(body); |
| return setMethod; |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| return null; |
| } |
| } |
| |
| private static void annotateWithNullableStuff(final PsiModifierListOwner field, final PsiModifierListOwner listOwner) |
| throws IncorrectOperationException { |
| final NullableNotNullManager manager = NullableNotNullManager.getInstance(field.getProject()); |
| final String notNull = manager.getNotNull(field); |
| if (notNull != null) { |
| annotate(listOwner, notNull); |
| } |
| else { |
| final String nullable = manager.getNullable(field); |
| if (nullable != null) { |
| annotate(listOwner, nullable); |
| } |
| } |
| |
| final PsiModifierList modifierList = listOwner.getModifierList(); |
| if (modifierList.hasExplicitModifier(GrModifier.DEF)) { |
| LOG.assertTrue(modifierList instanceof GrModifierList); |
| if (modifierList.getAnnotations().length > 0 || ((GrModifierList)modifierList).getModifiers().length > 1) { |
| ((GrModifierList)modifierList).setModifierProperty(GrModifier.DEF, false); |
| } |
| } |
| } |
| |
| private static void annotate(final PsiModifierListOwner listOwner, final String annotationQName) |
| throws IncorrectOperationException { |
| final PsiModifierList modifierList = listOwner.getModifierList(); |
| LOG.assertTrue(modifierList != null); |
| modifierList.addAnnotation(annotationQName); |
| } |
| |
| |
| } |