| /* |
| * 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 com.intellij.psi.impl.source.codeStyle; |
| |
| import com.intellij.lang.ASTNode; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.pom.java.LanguageLevel; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.*; |
| import com.intellij.psi.impl.CheckUtil; |
| import com.intellij.psi.impl.search.MethodDeepestSuperSearcher; |
| import com.intellij.psi.impl.source.SourceTreeToPsiMap; |
| import com.intellij.psi.impl.source.jsp.jspJava.JspxImportStatement; |
| import com.intellij.psi.statistics.JavaStatisticsManager; |
| import com.intellij.psi.util.*; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.Processor; |
| import com.intellij.util.containers.ContainerUtil; |
| import gnu.trove.THashSet; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.beans.Introspector; |
| import java.util.*; |
| |
| /** |
| * @author max |
| */ |
| public class JavaCodeStyleManagerImpl extends JavaCodeStyleManager { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.codeStyle.JavaCodeStyleManagerImpl"); |
| |
| @NonNls private static final String IMPL_SUFFIX = "Impl"; |
| @NonNls private static final String GET_PREFIX = "get"; |
| @NonNls private static final String IS_PREFIX = "is"; |
| @NonNls private static final String FIND_PREFIX = "find"; |
| @NonNls private static final String CREATE_PREFIX = "create"; |
| @NonNls private static final String SET_PREFIX = "set"; |
| |
| private final Project myProject; |
| |
| public JavaCodeStyleManagerImpl(final Project project) { |
| myProject = project; |
| } |
| |
| @Override |
| public PsiElement shortenClassReferences(@NotNull PsiElement element) throws IncorrectOperationException { |
| return shortenClassReferences(element, 0); |
| } |
| |
| @Override |
| public PsiElement shortenClassReferences(@NotNull PsiElement element, int flags) throws IncorrectOperationException { |
| CheckUtil.checkWritable(element); |
| if (!SourceTreeToPsiMap.hasTreeElement(element)) return element; |
| |
| final boolean addImports = (flags & DO_NOT_ADD_IMPORTS) == 0; |
| final boolean incompleteCode = (flags & UNCOMPLETE_CODE) != 0; |
| |
| final ReferenceAdjuster adjuster = ReferenceAdjuster.Extension.getReferenceAdjuster(element.getLanguage()); |
| if (adjuster != null) { |
| final ASTNode reference = adjuster.process(element.getNode(), addImports, incompleteCode, myProject); |
| return SourceTreeToPsiMap.treeToPsiNotNull(reference); |
| } |
| else { |
| return element; |
| } |
| } |
| |
| @Override |
| public void shortenClassReferences(@NotNull PsiElement element, int startOffset, int endOffset) throws IncorrectOperationException { |
| CheckUtil.checkWritable(element); |
| if (SourceTreeToPsiMap.hasTreeElement(element)) { |
| final ReferenceAdjuster adjuster = ReferenceAdjuster.Extension.getReferenceAdjuster(element.getLanguage()); |
| if (adjuster != null) { |
| adjuster.processRange(element.getNode(), startOffset, endOffset, myProject); |
| } |
| } |
| } |
| |
| @Override |
| public PsiElement qualifyClassReferences(@NotNull PsiElement element) { |
| final ReferenceAdjuster adjuster = ReferenceAdjuster.Extension.getReferenceAdjuster(element.getLanguage()); |
| if (adjuster != null) { |
| final ASTNode reference = adjuster.process(element.getNode(), false, false, true, true); |
| return SourceTreeToPsiMap.treeToPsiNotNull(reference); |
| } |
| return element; |
| } |
| |
| @Override |
| public void optimizeImports(@NotNull PsiFile file) throws IncorrectOperationException { |
| CheckUtil.checkWritable(file); |
| if (file instanceof PsiJavaFile) { |
| PsiImportList newList = prepareOptimizeImportsResult((PsiJavaFile)file); |
| if (newList != null) { |
| final PsiImportList importList = ((PsiJavaFile)file).getImportList(); |
| if (importList != null) { |
| importList.replace(newList); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public PsiImportList prepareOptimizeImportsResult(@NotNull PsiJavaFile file) { |
| return new ImportHelper(getSettings()).prepareOptimizeImportsResult(file); |
| } |
| |
| @Override |
| public boolean addImport(@NotNull PsiJavaFile file, @NotNull PsiClass refClass) { |
| return new ImportHelper(getSettings()).addImport(file, refClass); |
| } |
| |
| @Override |
| public void removeRedundantImports(@NotNull final PsiJavaFile file) throws IncorrectOperationException { |
| final Collection<PsiImportStatementBase> redundant = findRedundantImports(file); |
| if (redundant == null) return; |
| |
| for (final PsiImportStatementBase importStatement : redundant) { |
| final PsiJavaCodeReferenceElement ref = importStatement.getImportReference(); |
| //Do not remove non-resolving refs |
| if (ref == null || ref.resolve() == null) { |
| continue; |
| } |
| |
| importStatement.delete(); |
| } |
| } |
| |
| @Override |
| @Nullable |
| public Collection<PsiImportStatementBase> findRedundantImports(final PsiJavaFile file) { |
| final PsiImportList importList = file.getImportList(); |
| if (importList == null) return null; |
| final PsiImportStatementBase[] imports = importList.getAllImportStatements(); |
| if( imports.length == 0 ) return null; |
| |
| Set<PsiImportStatementBase> allImports = new THashSet<PsiImportStatementBase>(Arrays.asList(imports)); |
| final Collection<PsiImportStatementBase> redundant; |
| if (FileTypeUtils.isInServerPageFile(file)) { |
| // remove only duplicate imports |
| redundant = ContainerUtil.newIdentityTroveSet(); |
| ContainerUtil.addAll(redundant, imports); |
| redundant.removeAll(allImports); |
| for (PsiImportStatementBase importStatement : imports) { |
| if (importStatement instanceof JspxImportStatement && importStatement.isForeignFileImport()) { |
| redundant.remove(importStatement); |
| } |
| } |
| } |
| else { |
| redundant = allImports; |
| final List<PsiFile> roots = file.getViewProvider().getAllFiles(); |
| for (PsiElement root : roots) { |
| root.accept(new JavaRecursiveElementWalkingVisitor() { |
| @Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { |
| if (!reference.isQualified()) { |
| final JavaResolveResult resolveResult = reference.advancedResolve(false); |
| if (!inTheSamePackage(file, resolveResult.getElement())) { |
| final PsiElement resolveScope = resolveResult.getCurrentFileResolveScope(); |
| if (resolveScope instanceof PsiImportStatementBase) { |
| final PsiImportStatementBase importStatementBase = (PsiImportStatementBase)resolveScope; |
| redundant.remove(importStatementBase); |
| } |
| } |
| } |
| super.visitReferenceElement(reference); |
| } |
| |
| private boolean inTheSamePackage(PsiJavaFile file, PsiElement element) { |
| if (element instanceof PsiClass && ((PsiClass)element).getContainingClass() == null) { |
| final PsiFile containingFile = element.getContainingFile(); |
| if (containingFile instanceof PsiJavaFile) { |
| return Comparing.strEqual(file.getPackageName(), ((PsiJavaFile)containingFile).getPackageName()); |
| } |
| } |
| return false; |
| } |
| }); |
| } |
| } |
| return redundant; |
| } |
| |
| @Override |
| public int findEntryIndex(@NotNull PsiImportStatementBase statement) { |
| return new ImportHelper(getSettings()).findEntryIndex(statement); |
| } |
| |
| @Override |
| public SuggestedNameInfo suggestCompiledParameterName(@NotNull PsiType type) { |
| // avoid hang due to nice name evaluation that uses indices for resolve (IDEA-116803) |
| return new SuggestedNameInfo(suggestVariableNameByType(type, VariableKind.PARAMETER, true, true)) { |
| }; |
| } |
| |
| @Override |
| public SuggestedNameInfo suggestVariableName(@NotNull final VariableKind kind, |
| @Nullable final String propertyName, |
| @Nullable final PsiExpression expr, |
| @Nullable PsiType type, |
| final boolean correctKeywords) { |
| LinkedHashSet<String> names = new LinkedHashSet<String>(); |
| |
| if (expr != null && type == null) { |
| type = expr.getType(); |
| } |
| |
| if (propertyName != null) { |
| String[] namesByName = getSuggestionsByName(propertyName, kind, false, correctKeywords); |
| sortVariableNameSuggestions(namesByName, kind, propertyName, null); |
| ContainerUtil.addAll(names, namesByName); |
| } |
| |
| final NamesByExprInfo namesByExpr; |
| if (expr != null) { |
| namesByExpr = suggestVariableNameByExpression(expr, kind, correctKeywords); |
| if (namesByExpr.propertyName != null) { |
| sortVariableNameSuggestions(namesByExpr.names, kind, namesByExpr.propertyName, null); |
| } |
| ContainerUtil.addAll(names, namesByExpr.names); |
| } |
| else { |
| namesByExpr = null; |
| } |
| |
| if (type != null) { |
| String[] namesByType = suggestVariableNameByType(type, kind, correctKeywords); |
| sortVariableNameSuggestions(namesByType, kind, null, type); |
| ContainerUtil.addAll(names, namesByType); |
| } |
| |
| final String _propertyName; |
| if (propertyName != null) { |
| _propertyName = propertyName; |
| } |
| else { |
| _propertyName = namesByExpr != null ? namesByExpr.propertyName : null; |
| } |
| |
| addNamesFromStatistics(names, kind, _propertyName, type); |
| |
| String[] namesArray = ArrayUtil.toStringArray(names); |
| sortVariableNameSuggestions(namesArray, kind, _propertyName, type); |
| |
| final PsiType _type = type; |
| return new SuggestedNameInfo(namesArray) { |
| @Override |
| public void nameChosen(String name) { |
| if (_propertyName != null || _type != null && _type.isValid()) { |
| JavaStatisticsManager.incVariableNameUseCount(name, kind, _propertyName, _type); |
| } |
| } |
| }; |
| } |
| |
| private static void addNamesFromStatistics(Set<String> names, VariableKind variableKind, @Nullable String propertyName, @Nullable PsiType type) { |
| String[] allNames = JavaStatisticsManager.getAllVariableNamesUsed(variableKind, propertyName, type); |
| |
| int maxFrequency = 0; |
| for (String name : allNames) { |
| int count = JavaStatisticsManager.getVariableNameUseCount(name, variableKind, propertyName, type); |
| maxFrequency = Math.max(maxFrequency, count); |
| } |
| |
| int frequencyLimit = Math.max(5, maxFrequency / 2); |
| |
| for (String name : allNames) { |
| if( names.contains( name ) ) |
| { |
| continue; |
| } |
| int count = JavaStatisticsManager.getVariableNameUseCount(name, variableKind, propertyName, type); |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("new name:" + name + " count:" + count); |
| LOG.debug("frequencyLimit:" + frequencyLimit); |
| } |
| if (count >= frequencyLimit) { |
| names.add(name); |
| } |
| } |
| |
| if (propertyName != null && type != null) { |
| addNamesFromStatistics(names, variableKind, propertyName, null); |
| addNamesFromStatistics(names, variableKind, null, type); |
| } |
| } |
| |
| private String[] suggestVariableNameByType(PsiType type, final VariableKind variableKind, boolean correctKeywords) { |
| return suggestVariableNameByType(type, variableKind, correctKeywords, false); |
| } |
| |
| private String[] suggestVariableNameByType(final PsiType type, final VariableKind variableKind, final boolean correctKeywords, boolean skipIndices) { |
| String longTypeName = skipIndices ? type.getCanonicalText():getLongTypeName(type); |
| CodeStyleSettings.TypeToNameMap map = getMapByVariableKind(variableKind); |
| if (map != null && longTypeName != null) { |
| if (type.equals(PsiType.NULL)) { |
| longTypeName = CommonClassNames.JAVA_LANG_OBJECT; |
| } |
| String name = map.nameByType(longTypeName); |
| if (name != null && isIdentifier(name)) { |
| return new String[]{name}; |
| } |
| } |
| |
| final Collection<String> suggestions = new LinkedHashSet<String>(); |
| |
| final PsiClass psiClass = !skipIndices && type instanceof PsiClassType ? ((PsiClassType)type).resolve() : null; |
| |
| if (!skipIndices) { |
| suggestNamesForCollectionInheritors(type, variableKind, suggestions, correctKeywords); |
| |
| if (psiClass != null && CommonClassNames.JAVA_UTIL_OPTIONAL.equals(psiClass.getQualifiedName()) && ((PsiClassType)type).getParameterCount() == 1) { |
| PsiType optionalContent = ((PsiClassType)type).getParameters()[0]; |
| Collections.addAll(suggestions, suggestVariableNameByType(optionalContent, variableKind, correctKeywords, false)); |
| } |
| |
| suggestNamesFromGenericParameters(type, variableKind, suggestions, correctKeywords); |
| } |
| |
| String typeName = getTypeName(type, !skipIndices); |
| |
| if (typeName != null) { |
| typeName = normalizeTypeName(typeName); |
| ContainerUtil.addAll(suggestions, getSuggestionsByName(typeName, variableKind, type instanceof PsiArrayType, correctKeywords)); |
| } |
| |
| if (psiClass != null && psiClass.getContainingClass() != null) { |
| InheritanceUtil.processSupers(psiClass, false, new Processor<PsiClass>() { |
| @Override |
| public boolean process(PsiClass superClass) { |
| if (PsiTreeUtil.isAncestor(superClass, psiClass, true)) { |
| ContainerUtil.addAll(suggestions, getSuggestionsByName(superClass.getName(), variableKind, false, correctKeywords)); |
| } |
| return false; |
| } |
| }); |
| } |
| |
| return ArrayUtil.toStringArray(suggestions); |
| } |
| |
| private void suggestNamesFromGenericParameters(final PsiType type, |
| final VariableKind variableKind, |
| final Collection<String> suggestions, boolean correctKeywords) { |
| if (!(type instanceof PsiClassType)) { |
| return; |
| } |
| StringBuilder fullNameBuilder = new StringBuilder(); |
| final PsiType[] parameters = ((PsiClassType)type).getParameters(); |
| for (PsiType parameter : parameters) { |
| if (parameter instanceof PsiClassType) { |
| final String typeName = normalizeTypeName(getTypeName(parameter)); |
| if (typeName != null) { |
| fullNameBuilder.append(typeName); |
| } |
| } |
| } |
| String baseName = normalizeTypeName(getTypeName(type)); |
| if (baseName != null) { |
| fullNameBuilder.append(baseName); |
| ContainerUtil.addAll(suggestions, getSuggestionsByName(fullNameBuilder.toString(), variableKind, false, correctKeywords)); |
| } |
| } |
| |
| private void suggestNamesForCollectionInheritors(final PsiType type, |
| final VariableKind variableKind, |
| final Collection<String> suggestions, |
| final boolean correctKeywords) { |
| PsiType componentType = PsiUtil.extractIterableTypeParameter(type, false); |
| if (componentType == null || componentType.equals(type)) { |
| return; |
| } |
| String typeName = normalizeTypeName(getTypeName(componentType)); |
| if (typeName != null) { |
| ContainerUtil.addAll(suggestions, getSuggestionsByName(typeName, variableKind, true, correctKeywords)); |
| } |
| } |
| |
| @Nullable |
| private static String normalizeTypeName(String typeName) { |
| if (typeName == null) { |
| return null; |
| } |
| if (typeName.endsWith(IMPL_SUFFIX) && typeName.length() > IMPL_SUFFIX.length()) { |
| return typeName.substring(0, typeName.length() - IMPL_SUFFIX.length()); |
| } |
| return typeName; |
| } |
| |
| @Nullable |
| private static String getTypeName(PsiType type) { |
| return getTypeName(type, true); |
| } |
| |
| @Nullable |
| private static String getTypeName(PsiType type, boolean withIndices) { |
| type = type.getDeepComponentType(); |
| if (type instanceof PsiClassType) { |
| final PsiClassType classType = (PsiClassType)type; |
| final String className = classType.getClassName(); |
| if (className != null || !withIndices) return className; |
| final PsiClass aClass = classType.resolve(); |
| return aClass instanceof PsiAnonymousClass ? ((PsiAnonymousClass)aClass).getBaseClassType().getClassName() : null; |
| } |
| else if (type instanceof PsiPrimitiveType) { |
| return type.getPresentableText(); |
| } |
| else if (type instanceof PsiWildcardType) { |
| return getTypeName(((PsiWildcardType)type).getExtendsBound(), withIndices); |
| } |
| else if (type instanceof PsiIntersectionType) { |
| return getTypeName(((PsiIntersectionType)type).getRepresentative(), withIndices); |
| } |
| else if (type instanceof PsiCapturedWildcardType) { |
| return getTypeName(((PsiCapturedWildcardType)type).getWildcard(), withIndices); |
| } |
| else if (type instanceof PsiDisjunctionType) { |
| return getTypeName(((PsiDisjunctionType)type).getLeastUpperBound(), withIndices); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| @Nullable |
| private static String getLongTypeName(PsiType type) { |
| if (type instanceof PsiClassType) { |
| PsiClass aClass = ((PsiClassType)type).resolve(); |
| if (aClass == null) { |
| return null; |
| } |
| else if (aClass instanceof PsiAnonymousClass) { |
| PsiClass baseClass = ((PsiAnonymousClass)aClass).getBaseClassType().resolve(); |
| return baseClass != null ? baseClass.getQualifiedName() : null; |
| } |
| else { |
| return aClass.getQualifiedName(); |
| } |
| } |
| else if (type instanceof PsiArrayType) { |
| return getLongTypeName(((PsiArrayType)type).getComponentType()) + "[]"; |
| } |
| else if (type instanceof PsiPrimitiveType) { |
| return type.getPresentableText(); |
| } |
| else if (type instanceof PsiWildcardType) { |
| final PsiType bound = ((PsiWildcardType)type).getBound(); |
| return bound != null ? getLongTypeName(bound) : CommonClassNames.JAVA_LANG_OBJECT; |
| } |
| else if (type instanceof PsiCapturedWildcardType) { |
| final PsiType bound = ((PsiCapturedWildcardType)type).getWildcard().getBound(); |
| return bound != null ? getLongTypeName(bound) : CommonClassNames.JAVA_LANG_OBJECT; |
| } |
| else if (type instanceof PsiIntersectionType) { |
| return getLongTypeName(((PsiIntersectionType)type).getRepresentative()); |
| } |
| else if (type instanceof PsiDisjunctionType) { |
| return getLongTypeName(((PsiDisjunctionType)type).getLeastUpperBound()); |
| } |
| else { |
| return null; |
| } |
| } |
| |
| private static class NamesByExprInfo { |
| final String[] names; |
| final String propertyName; |
| |
| public NamesByExprInfo(String propertyName, String... names) { |
| this.names = names; |
| this.propertyName = propertyName; |
| } |
| } |
| |
| private NamesByExprInfo suggestVariableNameByExpression(PsiExpression expr, VariableKind variableKind, boolean correctKeywords) { |
| |
| final LinkedHashSet<String> names = new LinkedHashSet<String>(); |
| final String[] fromLiterals = suggestVariableNameFromLiterals(expr, variableKind, correctKeywords); |
| if (fromLiterals != null) { |
| ContainerUtil.addAll(names, fromLiterals); |
| } |
| |
| ContainerUtil.addAll(names, suggestVariableNameByExpressionOnly(expr, variableKind, correctKeywords, false).names); |
| ContainerUtil.addAll(names, suggestVariableNameByExpressionPlace(expr, variableKind, correctKeywords).names); |
| |
| PsiType type = expr.getType(); |
| if (type != null) { |
| ContainerUtil.addAll(names, suggestVariableNameByType(type, variableKind, correctKeywords)); |
| } |
| ContainerUtil.addAll(names, suggestVariableNameByExpressionOnly(expr, variableKind, correctKeywords, true).names); |
| |
| String[] namesArray = ArrayUtil.toStringArray(names); |
| String propertyName = suggestVariableNameByExpressionOnly(expr, variableKind, correctKeywords, false).propertyName != null ? suggestVariableNameByExpressionOnly(expr, variableKind, correctKeywords, false).propertyName : suggestVariableNameByExpressionPlace(expr, variableKind, correctKeywords).propertyName; |
| return new NamesByExprInfo(propertyName, namesArray); |
| } |
| |
| @Nullable |
| private String[] suggestVariableNameFromLiterals(PsiExpression expr, VariableKind variableKind, boolean correctKeywords) { |
| final PsiElement[] literals = PsiTreeUtil.collectElements(expr, new PsiElementFilter() { |
| @Override |
| public boolean isAccepted(PsiElement element) { |
| if (isStringPsiLiteral(element) && StringUtil.isJavaIdentifier(StringUtil.unquoteString(element.getText()))) { |
| final PsiElement exprList = element.getParent(); |
| if (exprList instanceof PsiExpressionList) { |
| final PsiElement call = exprList.getParent(); |
| if (call instanceof PsiNewExpression) { |
| return true; |
| } else if (call instanceof PsiMethodCallExpression) { |
| //TODO: exclude or not getA().getB("name").getC(); or getA(getB("name").getC()); It works fine for now in the most cases |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| }); |
| |
| if (literals.length == 1) { |
| final String text = StringUtil.unquoteString(literals[0].getText()); |
| return getSuggestionsByName(text, variableKind, expr.getType() instanceof PsiArrayType, correctKeywords); |
| } |
| return null; |
| } |
| |
| private NamesByExprInfo suggestVariableNameByExpressionOnly(PsiExpression expr, final VariableKind variableKind, boolean correctKeywords, boolean useAllMethodNames) { |
| if (expr instanceof PsiMethodCallExpression) { |
| PsiReferenceExpression methodExpr = ((PsiMethodCallExpression)expr).getMethodExpression(); |
| String methodName = methodExpr.getReferenceName(); |
| if (methodName != null) { |
| if ("of".equals(methodName) || "ofNullable".equals(methodName)) { |
| if (isJavaUtilMethodCall((PsiMethodCallExpression)expr)) { |
| PsiExpression[] expressions = ((PsiMethodCallExpression)expr).getArgumentList().getExpressions(); |
| if (expressions.length > 0) { |
| return suggestVariableNameByExpressionOnly(expressions[0], variableKind, correctKeywords, useAllMethodNames); |
| } |
| } |
| } |
| if ("map".equals(methodName) || "flatMap".equals(methodName) || "filter".equals(methodName)) { |
| if (isJavaUtilMethodCall((PsiMethodCallExpression)expr)) { |
| return new NamesByExprInfo(null); |
| } |
| } |
| |
| String[] words = NameUtil.nameToWords(methodName); |
| if (words.length > 0) { |
| final String firstWord = words[0]; |
| if (GET_PREFIX.equals(firstWord) |
| || IS_PREFIX.equals(firstWord) |
| || FIND_PREFIX.equals(firstWord) |
| || CREATE_PREFIX.equals(firstWord)) { |
| if (words.length > 1) { |
| final String propertyName = methodName.substring(firstWord.length()); |
| String[] names = getSuggestionsByName(propertyName, variableKind, false, correctKeywords); |
| final PsiExpression qualifierExpression = methodExpr.getQualifierExpression(); |
| if (qualifierExpression instanceof PsiReferenceExpression && |
| ((PsiReferenceExpression)qualifierExpression).resolve() instanceof PsiVariable) { |
| names = ArrayUtil.append(names, StringUtil |
| .sanitizeJavaIdentifier(changeIfNotIdentifier(qualifierExpression.getText() + StringUtil.capitalize(propertyName)))); |
| } |
| return new NamesByExprInfo(propertyName, names); |
| } |
| } |
| else if (words.length == 1 || useAllMethodNames) { |
| return new NamesByExprInfo(methodName, getSuggestionsByName(methodName, variableKind, false, correctKeywords)); |
| } |
| } |
| } |
| } |
| else if (expr instanceof PsiReferenceExpression) { |
| String propertyName = ((PsiReferenceExpression)expr).getReferenceName(); |
| PsiElement refElement = ((PsiReferenceExpression)expr).resolve(); |
| if (refElement instanceof PsiVariable) { |
| VariableKind refVariableKind = getVariableKind((PsiVariable)refElement); |
| propertyName = variableNameToPropertyName(propertyName, refVariableKind); |
| } |
| if (refElement != null && propertyName != null) { |
| String[] names = getSuggestionsByName(propertyName, variableKind, false, correctKeywords); |
| return new NamesByExprInfo(propertyName, names); |
| } |
| } |
| else if (expr instanceof PsiArrayAccessExpression) { |
| PsiExpression arrayExpr = ((PsiArrayAccessExpression)expr).getArrayExpression(); |
| if (arrayExpr instanceof PsiReferenceExpression) { |
| String arrayName = ((PsiReferenceExpression)arrayExpr).getReferenceName(); |
| PsiElement refElement = ((PsiReferenceExpression)arrayExpr).resolve(); |
| if (refElement instanceof PsiVariable) { |
| VariableKind refVariableKind = getVariableKind((PsiVariable)refElement); |
| arrayName = variableNameToPropertyName(arrayName, refVariableKind); |
| } |
| |
| if (arrayName != null) { |
| String name = StringUtil.unpluralize(arrayName); |
| if (name != null) { |
| String[] names = getSuggestionsByName(name, variableKind, false, correctKeywords); |
| return new NamesByExprInfo(name, names); |
| } |
| } |
| } |
| } |
| else if (expr instanceof PsiLiteralExpression && variableKind == VariableKind.STATIC_FINAL_FIELD) { |
| final PsiLiteralExpression literalExpression = (PsiLiteralExpression)expr; |
| final Object value = literalExpression.getValue(); |
| if (value instanceof String) { |
| final String stringValue = (String)value; |
| String[] names = getSuggestionsByValue(stringValue); |
| if (names.length > 0) { |
| return new NamesByExprInfo(null, constantValueToConstantName(names)); |
| } |
| } |
| } else if (expr instanceof PsiParenthesizedExpression) { |
| return suggestVariableNameByExpressionOnly(((PsiParenthesizedExpression)expr).getExpression(), variableKind, correctKeywords, useAllMethodNames); |
| } else if (expr instanceof PsiTypeCastExpression) { |
| return suggestVariableNameByExpressionOnly(((PsiTypeCastExpression)expr).getOperand(), variableKind, correctKeywords, useAllMethodNames); |
| } else if (expr instanceof PsiLiteralExpression) { |
| final String text = StringUtil.stripQuotesAroundValue(expr.getText()); |
| if (isIdentifier(text)) { |
| return new NamesByExprInfo(text, getSuggestionsByName(text, variableKind, false, correctKeywords)); |
| } |
| } |
| |
| return new NamesByExprInfo(null, ArrayUtil.EMPTY_STRING_ARRAY); |
| } |
| |
| private static boolean isJavaUtilMethodCall(PsiMethodCallExpression expr) { |
| PsiMethod method = expr.resolveMethod(); |
| if (method == null) return false; |
| |
| return isJavaUtilMethod(method) || !MethodDeepestSuperSearcher.processDeepestSuperMethods(method, new Processor<PsiMethod>() { |
| @Override |
| public boolean process(PsiMethod method) { |
| return !isJavaUtilMethod(method); |
| } |
| }); |
| } |
| |
| private static boolean isJavaUtilMethod(PsiMethod method) { |
| String name = PsiUtil.getMemberQualifiedName(method); |
| return name != null && name.startsWith("java.util."); |
| } |
| |
| private static String constantValueToConstantName(final String[] names) { |
| final StringBuilder result = new StringBuilder(); |
| for (int i = 0; i < names.length; i++) { |
| if (i > 0) result.append("_"); |
| result.append(names[i]); |
| } |
| return result.toString(); |
| } |
| |
| private static String[] getSuggestionsByValue(final String stringValue) { |
| List<String> result = new ArrayList<String>(); |
| StringBuffer currentWord = new StringBuffer(); |
| |
| boolean prevIsUpperCase = false; |
| |
| for (int i = 0; i < stringValue.length(); i++) { |
| final char c = stringValue.charAt(i); |
| if (Character.isUpperCase(c)) { |
| if (currentWord.length() > 0 && !prevIsUpperCase) { |
| result.add(currentWord.toString()); |
| currentWord = new StringBuffer(); |
| } |
| currentWord.append(c); |
| } else if (Character.isLowerCase(c)) { |
| currentWord.append(Character.toUpperCase(c)); |
| } else if (Character.isJavaIdentifierPart(c) && c != '_') { |
| if (Character.isJavaIdentifierStart(c) || currentWord.length() > 0 || !result.isEmpty()) { |
| currentWord.append(c); |
| } |
| } else { |
| if (currentWord.length() > 0) { |
| result.add(currentWord.toString()); |
| currentWord = new StringBuffer(); |
| } |
| } |
| |
| prevIsUpperCase = Character.isUpperCase(c); |
| } |
| |
| if (currentWord.length() > 0) { |
| result.add(currentWord.toString()); |
| } |
| return ArrayUtil.toStringArray(result); |
| } |
| |
| private NamesByExprInfo suggestVariableNameByExpressionPlace(PsiExpression expr, final VariableKind variableKind, boolean correctKeywords) { |
| if (expr.getParent() instanceof PsiExpressionList) { |
| PsiExpressionList list = (PsiExpressionList)expr.getParent(); |
| PsiElement listParent = list.getParent(); |
| PsiSubstitutor subst = PsiSubstitutor.EMPTY; |
| PsiMethod method = null; |
| if (listParent instanceof PsiMethodCallExpression) { |
| final JavaResolveResult resolveResult = ((PsiMethodCallExpression)listParent).getMethodExpression().advancedResolve(false); |
| method = (PsiMethod)resolveResult.getElement(); |
| subst = resolveResult.getSubstitutor(); |
| } |
| else { |
| if (listParent instanceof PsiAnonymousClass) { |
| listParent = listParent.getParent(); |
| } |
| if (listParent instanceof PsiNewExpression) { |
| method = ((PsiNewExpression)listParent).resolveConstructor(); |
| } |
| } |
| |
| if (method != null) { |
| final PsiElement navElement = method.getNavigationElement(); |
| if (navElement instanceof PsiMethod) { |
| method = (PsiMethod)navElement; |
| } |
| PsiExpression[] expressions = list.getExpressions(); |
| int index = -1; |
| for (int i = 0; i < expressions.length; i++) { |
| if (expressions[i] == expr) { |
| index = i; |
| break; |
| } |
| } |
| PsiParameter[] parameters = method.getParameterList().getParameters(); |
| if (index < parameters.length) { |
| String name = parameters[index].getName(); |
| if (name != null && TypeConversionUtil.areTypesAssignmentCompatible(subst.substitute(parameters[index].getType()), expr)) { |
| name = variableNameToPropertyName(name, VariableKind.PARAMETER); |
| String[] names = getSuggestionsByName(name, variableKind, false, correctKeywords); |
| if (expressions.length == 1) { |
| final String methodName = method.getName(); |
| String[] words = NameUtil.nameToWords(methodName); |
| if (words.length > 0) { |
| final String firstWord = words[0]; |
| if (SET_PREFIX.equals(firstWord)) { |
| final String propertyName = methodName.substring(firstWord.length()); |
| final String[] setterNames = getSuggestionsByName(propertyName, variableKind, false, correctKeywords); |
| names = ArrayUtil.mergeArrays(names, setterNames); |
| } |
| } |
| } |
| return new NamesByExprInfo(name, names); |
| } |
| } |
| } |
| } |
| else if (expr.getParent() instanceof PsiAssignmentExpression && variableKind == VariableKind.PARAMETER) { |
| final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expr.getParent(); |
| if (expr == assignmentExpression.getRExpression()) { |
| final PsiExpression leftExpression = assignmentExpression.getLExpression(); |
| if (leftExpression instanceof PsiReferenceExpression && ((PsiReferenceExpression) leftExpression).getQualifier() == null) { |
| String name = leftExpression.getText(); |
| if (name != null) { |
| final PsiElement resolve = ((PsiReferenceExpression)leftExpression).resolve(); |
| if (resolve instanceof PsiVariable) { |
| name = variableNameToPropertyName(name, getVariableKind((PsiVariable)resolve)); |
| } |
| String[] names = getSuggestionsByName(name, variableKind, false, correctKeywords); |
| return new NamesByExprInfo(name, names); |
| } |
| } |
| } |
| } |
| |
| return new NamesByExprInfo(null, ArrayUtil.EMPTY_STRING_ARRAY); |
| } |
| |
| @Override |
| public String variableNameToPropertyName(String name, VariableKind variableKind) { |
| if (variableKind == VariableKind.STATIC_FINAL_FIELD || variableKind == VariableKind.STATIC_FIELD && name.contains("_")) { |
| StringBuilder buffer = new StringBuilder(); |
| for (int i = 0; i < name.length(); i++) { |
| char c = name.charAt(i); |
| if (c != '_') { |
| if( Character.isLowerCase( c ) ) |
| { |
| return variableNameToPropertyNameInner( name, variableKind ); |
| } |
| |
| buffer.append(Character.toLowerCase(c)); |
| continue; |
| } |
| //noinspection AssignmentToForLoopParameter |
| i++; |
| if (i < name.length()) { |
| c = name.charAt(i); |
| buffer.append(c); |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| return variableNameToPropertyNameInner(name, variableKind); |
| } |
| |
| private String variableNameToPropertyNameInner(String name, VariableKind variableKind) { |
| String prefix = getPrefixByVariableKind(variableKind); |
| String suffix = getSuffixByVariableKind(variableKind); |
| boolean doDecapitalize = false; |
| |
| int pLength = prefix.length(); |
| if (pLength > 0 && name.startsWith(prefix) && name.length() > pLength && |
| // check it's not just a long camel word that happens to begin with the specified prefix |
| (!Character.isLetter(prefix.charAt(pLength - 1)) || Character.isUpperCase(name.charAt(pLength)))) { |
| name = name.substring(pLength); |
| doDecapitalize = true; |
| } |
| |
| if (name.endsWith(suffix) && name.length() > suffix.length()) { |
| name = name.substring(0, name.length() - suffix.length()); |
| doDecapitalize = true; |
| } |
| |
| if (doDecapitalize) { |
| name = Introspector.decapitalize(name); |
| } |
| |
| return name; |
| } |
| |
| @Override |
| public String propertyNameToVariableName(String propertyName, VariableKind variableKind) { |
| if (variableKind == VariableKind.STATIC_FINAL_FIELD) { |
| String[] words = NameUtil.nameToWords(propertyName); |
| StringBuilder buffer = new StringBuilder(); |
| for (int i = 0; i < words.length; i++) { |
| String word = words[i]; |
| if (i > 0) { |
| buffer.append("_"); |
| } |
| buffer.append(StringUtil.toUpperCase(word)); |
| } |
| return buffer.toString(); |
| } |
| |
| String prefix = getPrefixByVariableKind(variableKind); |
| String name = propertyName; |
| if (!name.isEmpty() && !prefix.isEmpty() && !StringUtil.endsWithChar(prefix, '_')) { |
| name = Character.toUpperCase(name.charAt(0)) + name.substring(1); |
| } |
| name = prefix + name + getSuffixByVariableKind(variableKind); |
| name = changeIfNotIdentifier(name); |
| return name; |
| } |
| |
| private String[] getSuggestionsByName(String name, VariableKind variableKind, boolean isArray, boolean correctKeywords) { |
| boolean upperCaseStyle = variableKind == VariableKind.STATIC_FINAL_FIELD; |
| boolean preferLongerNames = getSettings().PREFER_LONGER_NAMES; |
| String prefix = getPrefixByVariableKind(variableKind); |
| String suffix = getSuffixByVariableKind(variableKind); |
| |
| List<String> answer = new ArrayList<String>(); |
| for (String suggestion : NameUtil.getSuggestionsByName(name, prefix, suffix, upperCaseStyle, preferLongerNames, isArray)) { |
| answer.add(correctKeywords ? changeIfNotIdentifier(suggestion) : suggestion); |
| } |
| |
| return ArrayUtil.toStringArray(answer); |
| } |
| |
| @Override |
| public String suggestUniqueVariableName(String baseName, PsiElement place, boolean lookForward) { |
| int index = 0; |
| PsiElement scope = PsiTreeUtil.getNonStrictParentOfType(place, PsiStatement.class, PsiCodeBlock.class, PsiMethod.class); |
| NextName: |
| while (true) { |
| String name = baseName; |
| if (index > 0) { |
| name += index; |
| } |
| index++; |
| if (PsiUtil.isVariableNameUnique(name, place)) { |
| if (lookForward) { |
| final String name1 = name; |
| PsiElement run = scope; |
| while (run != null) { |
| class CancelException extends RuntimeException { |
| } |
| try { |
| run.accept(new JavaRecursiveElementWalkingVisitor() { |
| @Override |
| public void visitAnonymousClass(final PsiAnonymousClass aClass) { |
| } |
| |
| @Override public void visitVariable(PsiVariable variable) { |
| if (name1.equals(variable.getName())) { |
| throw new CancelException(); |
| } |
| } |
| }); |
| } |
| catch (CancelException e) { |
| continue NextName; |
| } |
| run = run.getNextSibling(); |
| if (scope instanceof PsiMethod) {//do not check next member for param name conflict |
| break; |
| } |
| } |
| |
| } |
| return name; |
| } |
| } |
| } |
| |
| @Override |
| @NotNull |
| public SuggestedNameInfo suggestUniqueVariableName(@NotNull final SuggestedNameInfo baseNameInfo, |
| PsiElement place, |
| boolean ignorePlaceName, |
| boolean lookForward) { |
| final String[] names = baseNameInfo.names; |
| final LinkedHashSet<String> uniqueNames = new LinkedHashSet<String>(names.length); |
| for (String name : names) { |
| if (ignorePlaceName && place instanceof PsiNamedElement) { |
| final String placeName = ((PsiNamedElement)place).getName(); |
| if (Comparing.strEqual(placeName, name)) { |
| uniqueNames.add(name); |
| continue; |
| } |
| } |
| uniqueNames.add(suggestUniqueVariableName(name, place, lookForward)); |
| } |
| |
| return new SuggestedNameInfo(ArrayUtil.toStringArray(uniqueNames)) { |
| @Override |
| public void nameChosen(String name) { |
| baseNameInfo.nameChosen(name); |
| } |
| }; |
| } |
| |
| private static void sortVariableNameSuggestions(String[] names, |
| final VariableKind variableKind, |
| @Nullable final String propertyName, |
| @Nullable final PsiType type) { |
| if( names.length <= 1 ) { |
| return; |
| } |
| |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("sorting names:" + variableKind); |
| if (propertyName != null) { |
| LOG.debug("propertyName:" + propertyName); |
| } |
| if (type != null) { |
| LOG.debug("type:" + type); |
| } |
| for (String name : names) { |
| int count = JavaStatisticsManager.getVariableNameUseCount(name, variableKind, propertyName, type); |
| LOG.debug(name + " : " + count); |
| } |
| } |
| |
| Comparator<String> comparator = new Comparator<String>() { |
| @Override |
| public int compare(String s1, String s2) { |
| int count1 = JavaStatisticsManager.getVariableNameUseCount(s1, variableKind, propertyName, type); |
| int count2 = JavaStatisticsManager.getVariableNameUseCount(s2, variableKind, propertyName, type); |
| return count2 - count1; |
| } |
| }; |
| Arrays.sort(names, comparator); |
| } |
| |
| @Override |
| @NotNull |
| public String getPrefixByVariableKind(VariableKind variableKind) { |
| String prefix = ""; |
| switch (variableKind) { |
| case FIELD: |
| prefix = getSettings().FIELD_NAME_PREFIX; |
| break; |
| case STATIC_FIELD: |
| prefix = getSettings().STATIC_FIELD_NAME_PREFIX; |
| break; |
| case PARAMETER: |
| prefix = getSettings().PARAMETER_NAME_PREFIX; |
| break; |
| case LOCAL_VARIABLE: |
| prefix = getSettings().LOCAL_VARIABLE_NAME_PREFIX; |
| break; |
| case STATIC_FINAL_FIELD: |
| prefix = ""; |
| break; |
| default: |
| LOG.assertTrue(false); |
| break; |
| } |
| if (prefix == null) { |
| prefix = ""; |
| } |
| return prefix; |
| } |
| |
| @Override |
| @NotNull |
| public String getSuffixByVariableKind(VariableKind variableKind) { |
| String suffix = ""; |
| switch (variableKind) { |
| case FIELD: |
| suffix = getSettings().FIELD_NAME_SUFFIX; |
| break; |
| case STATIC_FIELD: |
| suffix = getSettings().STATIC_FIELD_NAME_SUFFIX; |
| break; |
| case PARAMETER: |
| suffix = getSettings().PARAMETER_NAME_SUFFIX; |
| break; |
| case LOCAL_VARIABLE: |
| suffix = getSettings().LOCAL_VARIABLE_NAME_SUFFIX; |
| break; |
| case STATIC_FINAL_FIELD: |
| suffix = ""; |
| break; |
| default: |
| LOG.assertTrue(false); |
| break; |
| } |
| if (suffix == null) { |
| suffix = ""; |
| } |
| return suffix; |
| } |
| |
| @Nullable |
| private CodeStyleSettings.TypeToNameMap getMapByVariableKind(VariableKind variableKind) { |
| if (variableKind == VariableKind.FIELD) return getSettings().FIELD_TYPE_TO_NAME; |
| if (variableKind == VariableKind.STATIC_FIELD) return getSettings().STATIC_FIELD_TYPE_TO_NAME; |
| if (variableKind == VariableKind.PARAMETER) return getSettings().PARAMETER_TYPE_TO_NAME; |
| if (variableKind == VariableKind.LOCAL_VARIABLE) return getSettings().LOCAL_VARIABLE_TYPE_TO_NAME; |
| return null; |
| } |
| |
| @NonNls |
| private String changeIfNotIdentifier(String name) { |
| if (!isIdentifier(name)) { |
| return StringUtil.fixVariableNameDerivedFromPropertyName(name); |
| } |
| return name; |
| } |
| |
| private boolean isIdentifier(String name) { |
| return PsiNameHelper.getInstance(myProject).isIdentifier(name, LanguageLevel.HIGHEST); |
| } |
| |
| private CodeStyleSettings getSettings() { |
| return CodeStyleSettingsManager.getSettings(myProject); |
| } |
| |
| public static boolean isStringPsiLiteral(PsiElement element) { |
| if (element instanceof PsiLiteralExpression) { |
| final String text = element.getText(); |
| return StringUtil.isQuotedString(text); |
| } |
| return false; |
| } |
| } |