blob: b1856c124e0ed7de1c28931fb73e2509c800a596 [file] [log] [blame]
/*
* 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.refactoring;
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.util.InheritanceUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyNamesUtil;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* @author ilyas
*/
public class GroovyNameSuggestionUtil {
public static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.refactoring.GroovyNameSuggestionUtil");
private GroovyNameSuggestionUtil() {
}
public static String[] suggestVariableNames(GrExpression expr, NameValidator validator) {
return suggestVariableNames(expr, validator, false);
}
public static String[] suggestVariableNames(@NotNull GrExpression expr, NameValidator validator, boolean forStaticVariable) {
Set<String> possibleNames = new LinkedHashSet<String>();
PsiType type = expr.getType();
generateNameByExpr(expr, possibleNames, validator, forStaticVariable);
if (type != null && !PsiType.VOID.equals(type)) {
generateVariableNameByTypeInner(type, possibleNames,validator);
}
possibleNames.remove("");
if (possibleNames.isEmpty()) {
possibleNames.add(validator.validateName("var", true));
}
return ArrayUtil.toStringArray(possibleNames);
}
public static String[] suggestVariableNameByType(PsiType type, NameValidator validator) {
if (type == null) return ArrayUtil.EMPTY_STRING_ARRAY;
Set<String> possibleNames = new LinkedHashSet<String>();
generateVariableNameByTypeInner(type, possibleNames, validator);
return ArrayUtil.toStringArray(possibleNames);
}
private static void generateVariableNameByTypeInner(PsiType type, Set<String> possibleNames, NameValidator validator) {
String unboxed = PsiTypesUtil.unboxIfPossible(type.getCanonicalText());
if (unboxed != null && !unboxed.equals(type.getCanonicalText())) {
String name = generateNameForBuiltInType(unboxed);
name = validator.validateName(name, true);
if (GroovyNamesUtil.isIdentifier(name)) {
possibleNames.add(name);
}
}
else if (type instanceof PsiIntersectionType) {
for (PsiType psiType : ((PsiIntersectionType)type).getConjuncts()) {
generateByType(psiType, possibleNames, validator);
}
}
else {
generateByType(type, possibleNames, validator);
}
}
private static void generateNameByExpr(GrExpression expr, Set<String> possibleNames, NameValidator validator, boolean forStaticVariable) {
if (expr instanceof GrReferenceExpression && ((GrReferenceExpression) expr).getReferenceName() != null) {
if (PsiUtil.isThisReference(expr)) {
possibleNames.add(validator.validateName("thisInstance", true));
}
if (PsiUtil.isSuperReference(expr)) {
possibleNames.add(validator.validateName("superInstance", true));
}
GrReferenceExpression refExpr = (GrReferenceExpression) expr;
String name = refExpr.getReferenceName();
if (name != null && name.toUpperCase().equals(name)) {
possibleNames.add(validator.validateName(name.toLowerCase(), true));
} else {
generateCamelNames(possibleNames, validator, name);
}
if (expr.getText().equals(name)) {
possibleNames.remove(name);
}
}
if (expr instanceof GrMethodCallExpression) {
generateNameByExpr(((GrMethodCallExpression) expr).getInvokedExpression(), possibleNames, validator, forStaticVariable);
}
if (expr instanceof GrLiteral) {
final Object value = ((GrLiteral)expr).getValue();
if (value instanceof String) {
generateNameByString(possibleNames, (String)value, validator, forStaticVariable, expr.getProject());
}
}
}
private static void generateNameByString(Set<String> possibleNames,
String value,
NameValidator validator,
boolean forStaticVariable,
Project project) {
if (!PsiNameHelper.getInstance(project).isIdentifier(value)) return;
if (forStaticVariable) {
StringBuilder buffer = new StringBuilder(value.length() + 10);
char[] chars = new char[value.length()];
value.getChars(0, value.length(), chars, 0);
boolean wasLow = Character.isLowerCase(chars[0]);
buffer.append(Character.toUpperCase(chars[0]));
for (int i = 1; i < chars.length; i++) {
if (Character.isUpperCase(chars[i])) {
if (wasLow) {
buffer.append('_');
wasLow = false;
}
}
else {
wasLow = true;
}
buffer.append(Character.toUpperCase(chars[i]));
}
possibleNames.add(validator.validateName(buffer.toString(), true));
}
else {
possibleNames.add(validator.validateName(value, true));
}
}
private static void generateByType(PsiType type, Set<String> possibleNames, NameValidator validator) {
String typeName = type.getPresentableText();
generateNamesForCollectionType(type, possibleNames, validator);
generateNamesForArrayType(type, possibleNames, validator);
generateNamesForExceptions(type, possibleNames, validator);
typeName = cleanTypeName(typeName);
if (typeName.equals("String")) {
possibleNames.add(validator.validateName("s", true));
}
if (typeName.equals("Closure")) {
possibleNames.add(validator.validateName("cl", true));
}
if (typeName.toUpperCase().equals(typeName)) {
possibleNames.add(validator.validateName(GroovyNamesUtil.deleteNonLetterFromString(typeName.toLowerCase()), true));
} else if (!typeName.equals(typeName.toLowerCase())) {
generateCamelNames(possibleNames, validator, typeName);
possibleNames.remove(typeName);
}
}
private static void generateNamesForExceptions(PsiType type, Set<String> possibleNames, NameValidator validator) {
if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ERROR)) {
possibleNames.add(validator.validateName("error", true));
}
else if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_EXCEPTION)) {
possibleNames.add(validator.validateName("e", true));
}
}
private static void generateNamesForArrayType(PsiType type, Set<String> possibleNames, NameValidator validator) {
int arrayDim = type.getArrayDimensions();
if (arrayDim == 0) return;
PsiType deepType = type.getDeepComponentType();
String candidateName = cleanTypeName(deepType.getPresentableText());
if (deepType instanceof PsiClassType) {
PsiClass clazz = ((PsiClassType) deepType).resolve();
if (clazz == null) return;
candidateName = GroovyNamesUtil.fromLowerLetter(clazz.getName());
}
candidateName = StringUtil.pluralize(GroovyNamesUtil.fromLowerLetter(candidateName));
generateCamelNames(possibleNames, validator, candidateName);
ArrayList<String> camelizedName = GroovyNamesUtil.camelizeString(candidateName);
candidateName = camelizedName.get(camelizedName.size() - 1);
candidateName = "arrayOf" + fromUpperLetter(candidateName);
possibleNames.add(validator.validateName(candidateName, true));
}
private static void generateNamesForCollectionType(PsiType type, Set<String> possibleNames, NameValidator validator) {
PsiType componentType = getCollectionComponentType(type, validator.getProject());
if (!(type instanceof PsiClassType) || componentType == null) return;
PsiClass clazz = ((PsiClassType) type).resolve();
if (clazz == null) return;
String collectionName = clazz.getName();
assert collectionName != null;
String componentName = cleanTypeName(componentType.getPresentableText());
if (componentType instanceof PsiClassType) {
PsiClassType classType = (PsiClassType) componentType;
PsiClass psiClass = classType.resolve();
if (psiClass == null) return;
componentName = psiClass.getName();
}
assert componentName != null;
String candidateName = StringUtil.pluralize(GroovyNamesUtil.fromLowerLetter(componentName));
generateCamelNames(possibleNames, validator, candidateName);
ArrayList<String> camelizedName = GroovyNamesUtil.camelizeString(candidateName);
candidateName = camelizedName.get(camelizedName.size() - 1);
candidateName = collectionName.toLowerCase() + "Of" + fromUpperLetter(candidateName);
possibleNames.add(validator.validateName(candidateName, true));
}
@NotNull
private static String cleanTypeName(@NotNull String typeName) {
if (typeName.contains(".")) {
typeName = typeName.substring(typeName.lastIndexOf(".") + 1);
}
if (typeName.contains("<")) {
typeName = typeName.substring(0, typeName.indexOf("<"));
}
return typeName;
}
private static void generateCamelNames(Set<String> possibleNames, NameValidator validator, String typeName) {
ArrayList<String> camelTokens = GroovyNamesUtil.camelizeString(typeName);
Collections.reverse(camelTokens);
if (!camelTokens.isEmpty()) {
String possibleName = "";
for (String camelToken : camelTokens) {
possibleName = camelToken + fromUpperLetter(possibleName);
String candidate = validator.validateName(possibleName, true);
// todo generify
if (candidate.equals("class")) {
candidate = validator.validateName("clazz", true);
}
if (!possibleNames.contains(candidate) && GroovyNamesUtil.isIdentifier(candidate)) {
possibleNames.add(candidate);
}
}
}
}
private static String generateNameForBuiltInType(String unboxed) {
return unboxed.toLowerCase().substring(0, 1);
}
private static String fromUpperLetter(String str) {
if (str.isEmpty()) return "";
if (str.length() == 1) return str.toUpperCase();
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
@Nullable
private static PsiType getCollectionComponentType(PsiType type, Project project) {
if (!(type instanceof PsiClassType)) return null;
PsiClassType classType = (PsiClassType) type;
PsiClassType.ClassResolveResult result = classType.resolveGenerics();
PsiClass clazz = result.getElement();
if (clazz == null) return null;
JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
@SuppressWarnings({"ConstantConditions"}) PsiClass collectionClass = facade.findClass("java.util.Collection", type.getResolveScope());
if (collectionClass == null || collectionClass.getTypeParameters().length != 1) return null;
PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(collectionClass, clazz, result.getSubstitutor());
if (substitutor == null) return null;
PsiType componentType = substitutor.substitute(collectionClass.getTypeParameters()[0]);
return componentType instanceof PsiIntersectionType ? null : componentType;
}
}