Simple guava -> java conversions using type migration. Supports only single methods invoked on a variable (not method chains). Do not validate and insert type parameters in most of cases

This commit is contained in:
Dmitry Batkovich
2015-09-30 12:06:34 +03:00
parent 216075da68
commit eb5b5a51ca
22 changed files with 880 additions and 21 deletions
@@ -16,7 +16,11 @@
package com.intellij.refactoring.typeMigration;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.typeMigration.usageInfo.TypeMigrationUsageInfo;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nullable;
public class TypeConversionDescriptorBase {
@@ -33,7 +37,26 @@ public class TypeConversionDescriptorBase {
myRoot = root;
}
public void replace(PsiExpression expression){}
/**
* @return converted expression type or null if not known
*/
@Nullable
public PsiType conversionType() {
return null;
}
/**
* @return substitutor of converted method parameters
* or null if expression is not method call expression
*/
@Nullable
public PsiSubstitutor getConvertedMethodParameters() {
return null;
}
public PsiExpression replace(PsiExpression expression) throws IncorrectOperationException {
return expression;
}
@Override
public String toString() {
@@ -331,6 +331,10 @@ public class TypeMigrationLabeler {
}
}
else if (expr instanceof PsiLambdaExpression) {
//TODO conversion of lambda expression now works incorrectly [Dmitry Batkovich]
return;
}
convertExpression(expr, migrationType, originalType, isCovariant);
}
@@ -40,7 +40,11 @@ public class TypeMigrationReplacementUtil {
public static void replaceExpression(PsiExpression expression, final Project project, Object conversion) {
if (conversion instanceof TypeConversionDescriptorBase) {
((TypeConversionDescriptorBase)conversion).replace(expression);
try {
((TypeConversionDescriptorBase)conversion).replace(expression);
} catch (IncorrectOperationException e) {
LOG.error(e);
}
} else if (conversion instanceof String) {
String replacement = (String)conversion;
try {
@@ -219,12 +219,13 @@ class TypeMigrationStatementProcessor extends JavaRecursiveElementVisitor {
myLabeler.markFailedConversion(typePair, qualifierExpression);
} else {
final PsiElement parent = Util.getEssentialParent(expression);
final PsiType type = conversion.conversionType();
if (parent instanceof PsiMethodCallExpression) {
myLabeler.setConversionMapping((PsiMethodCallExpression)parent, conversion);
myTypeEvaluator.setType(new TypeMigrationUsageInfo(parent), myTypeEvaluator.evaluateType((PsiExpression)parent));
myTypeEvaluator.setType(new TypeMigrationUsageInfo(parent), type != null ? type: myTypeEvaluator.evaluateType((PsiExpression)parent));
} else {
myLabeler.setConversionMapping(expression, conversion);
myTypeEvaluator.setType(new TypeMigrationUsageInfo(expression), myTypeEvaluator.evaluateType(expression));
myTypeEvaluator.setType(new TypeMigrationUsageInfo(expression), type != null ? type: myTypeEvaluator.evaluateType(expression));
}
}
}
@@ -7,6 +7,11 @@
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.BoxingTypeConversionRule"/>
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.ElementToArrayConversionRule"/>
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.ThreadLocalConversionRule"/>
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.guava.GuavaOptionalConversionRule"/>
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.guava.GuavaFluentIterableConversionRule"/>
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.guava.GuavaFunctionConversionRule"/>
<conversion.rule implementation="com.intellij.refactoring.typeMigration.rules.guava.GuavaPredicateConversionRule"/>
<intentionAction>
<className>com.intellij.refactoring.typeMigration.intentions.ConvertFieldToAtomicIntention</className>
<category>Java/Concurrency</category>
@@ -19,5 +24,11 @@
<className>com.intellij.refactoring.typeMigration.intentions.ChangeClassParametersIntention</className>
<category>Java/Declaration</category>
</intentionAction>
<localInspection groupPath="Java" language="JAVA" shortName="Guava"
groupBundle="messages.InspectionsBundle"
groupName="Java language level migration aids" enabledByDefault="false" level="WARNING"
implementationClass="com.intellij.refactoring.typeMigration.inspections.GuavaInspection"
displayName="Guava's functional primitives can be replaced by JAVA"/>
</extensions>
</idea-plugin>
@@ -5,12 +5,14 @@ import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiType;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.structuralsearch.MatchOptions;
import com.intellij.structuralsearch.plugin.replace.ReplaceOptions;
import com.intellij.structuralsearch.plugin.replace.impl.Replacer;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
@@ -25,10 +27,10 @@ public class TypeConversionDescriptor extends TypeConversionDescriptorBase {
private String myStringToReplace = null;
private String myReplaceByString = "$";
private PsiExpression myExpression;
private PsiType myConversionType;
public TypeConversionDescriptor(@NonNls final String stringToReplace, @NonNls final String replaceByString) {
myStringToReplace = stringToReplace;
myReplaceByString = replaceByString;
this(stringToReplace, replaceByString, (PsiExpression)null);
}
public TypeConversionDescriptor(@NonNls final String stringToReplace, @NonNls final String replaceByString, final PsiExpression expression) {
@@ -37,6 +39,16 @@ public class TypeConversionDescriptor extends TypeConversionDescriptorBase {
myExpression = expression;
}
public TypeConversionDescriptor(String stringToReplace, String replaceByString, PsiType conversionType) {
this(stringToReplace, replaceByString);
myConversionType = conversionType;
}
public TypeConversionDescriptor withConversionType(PsiType conversionType) {
myConversionType = conversionType;
return this;
}
public void setStringToReplace(String stringToReplace) {
myStringToReplace = stringToReplace;
}
@@ -61,8 +73,14 @@ public class TypeConversionDescriptor extends TypeConversionDescriptorBase {
myExpression = expression;
}
@Nullable
@Override
public void replace(PsiExpression expression) {
public PsiType conversionType() {
return myConversionType;
}
@Override
public PsiExpression replace(PsiExpression expression) {
if (getExpression() != null) expression = getExpression();
final Project project = expression.getProject();
final ReplaceOptions options = new ReplaceOptions();
@@ -70,19 +88,9 @@ public class TypeConversionDescriptor extends TypeConversionDescriptorBase {
matchOptions.setFileType(StdFileTypes.JAVA);
options.setMatchOptions(matchOptions);
final Replacer replacer = new Replacer(project, null);
try {
final String replacement = replacer.testReplace(expression.getText(), getStringToReplace(), getReplaceByString(), options);
try {
JavaCodeStyleManager.getInstance(project).shortenClassReferences(expression.replace(
final String replacement = replacer.testReplace(expression.getText(), getStringToReplace(), getReplaceByString(), options);
return (PsiExpression)JavaCodeStyleManager.getInstance(project).shortenClassReferences(expression.replace(
JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(replacement, expression)));
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
@Override
@@ -27,7 +27,12 @@ public class TypeMigrationVariableTypeFixProvider implements ChangeVariableTypeQ
@NotNull
public IntentionAction[] getFixes(@NotNull PsiVariable variable, @NotNull PsiType toReturn) {
return new IntentionAction[]{new VariableTypeFix(variable, toReturn) {
return new IntentionAction[]{createTypeMigrationFix(variable, toReturn)};
}
@NotNull
public static VariableTypeFix createTypeMigrationFix(@NotNull final PsiVariable variable, @NotNull final PsiType toReturn) {
return new VariableTypeFix(variable, toReturn) {
@NotNull
@Override
public String getText() {
@@ -61,6 +66,6 @@ public class TypeMigrationVariableTypeFixProvider implements ChangeVariableTypeQ
public boolean startInWriteAction() {
return true;
}
}};
};
}
}
@@ -0,0 +1,90 @@
/*
* Copyright 2000-2015 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.refactoring.typeMigration.inspections;
import com.intellij.codeInsight.daemon.impl.quickfix.VariableTypeFix;
import com.intellij.codeInspection.*;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.AtomicNotNullLazyValue;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.typeMigration.TypeMigrationVariableTypeFixProvider;
import com.intellij.refactoring.typeMigration.rules.TypeConversionRule;
import com.intellij.refactoring.typeMigration.rules.guava.BaseGuavaTypeConversionRule;
import com.intellij.util.containers.hash.HashMap;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author Dmitry Batkovich
*/
@SuppressWarnings("DialogTitleCapitalization")
public class GuavaInspection extends BaseJavaLocalInspectionTool {
private final static String PROBLEM_DESCRIPTION = "Guava's functional primitives can be replaced by Java API";
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) {
return PsiElementVisitor.EMPTY_VISITOR;
}
return new JavaElementVisitor() {
private final AtomicNotNullLazyValue<Map<String, String>> myGuavaClassConversions = new AtomicNotNullLazyValue<Map<String, String>>() {
@NotNull
@Override
protected Map<String, String> compute() {
Map<String, String> map = new HashMap<String, String>();
for (TypeConversionRule rule : TypeConversionRule.EP_NAME.getExtensions()) {
if (rule instanceof BaseGuavaTypeConversionRule) {
final String fromClass = ((BaseGuavaTypeConversionRule)rule).ruleFromClass();
final String toClass = ((BaseGuavaTypeConversionRule)rule).ruleToClass();
map.put(fromClass, toClass);
}
}
return map;
}
};
@Override
public void visitVariable(PsiVariable variable) {
final PsiType type = variable.getType();
if (type instanceof PsiClassType) {
final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics();
final PsiClass psiClass = resolveResult.getElement();
if (psiClass != null) {
final String qName = psiClass.getQualifiedName();
final String toQName = myGuavaClassConversions.getValue().get(qName);
if (toQName != null) {
final Project project = holder.getProject();
final JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
final PsiClass targetClass = javaPsiFacade.findClass(toQName, GlobalSearchScope.allScope(project));
if (targetClass != null) {
final Collection<PsiType> typeParameters = resolveResult.getSubstitutor().getSubstitutionMap().values();
final PsiClassType targetType =
javaPsiFacade.getElementFactory().createType(targetClass, typeParameters.toArray(new PsiType[typeParameters.size()]));
final VariableTypeFix fix = TypeMigrationVariableTypeFixProvider.createTypeMigrationFix(variable, targetType);
holder.registerProblem(variable, PROBLEM_DESCRIPTION, fix);
}
}
}
}
}
};
}
}
@@ -0,0 +1,128 @@
/*
* Copyright 2000-2015 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.refactoring.typeMigration.rules.guava;
import com.intellij.codeInspection.AnonymousCanBeLambdaInspection;
import com.intellij.psi.*;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import com.intellij.refactoring.typeMigration.rules.TypeConversionRule;
import com.intellij.reference.SoftLazyValue;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.hash.HashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public abstract class BaseGuavaTypeConversionRule extends TypeConversionRule {
private final SoftLazyValue<Map<String, TypeConversionDescriptorBase>> mySimpleDescriptors = new SoftLazyValue<Map<String, TypeConversionDescriptorBase>>() {
@NotNull
@Override
protected Map<String, TypeConversionDescriptorBase> compute() {
Map<String, TypeConversionDescriptorBase> map = new HashMap<String, TypeConversionDescriptorBase>();
fillSimpleDescriptors(map);
return map;
}
};
protected abstract void fillSimpleDescriptors(Map<String, TypeConversionDescriptorBase> descriptorsMap);
@Nullable
protected TypeConversionDescriptorBase findConversionForMethod(@NotNull PsiType from,
@NotNull PsiType to,
@NotNull PsiMethod method,
String methodName,
PsiExpression context,
TypeMigrationLabeler labeler) {
return null;
};
@NotNull
public abstract String ruleFromClass();
@NotNull
public abstract String ruleToClass();
@Nullable
@Override
public final TypeConversionDescriptorBase findConversion(PsiType from,
PsiType to,
PsiMember member,
PsiExpression context,
TypeMigrationLabeler labeler) {
if (!canConvert(from, to)) {
return null;
}
if (member instanceof PsiMethod) {
PsiMethod method = (PsiMethod)member;
final String methodName = method.getName();
final TypeConversionDescriptorBase descriptor = mySimpleDescriptors.getValue().get(methodName);
if (descriptor != null) {
return descriptor;
}
return findConversionForMethod(from, to, method, methodName, context, labeler);
} else if (context instanceof PsiNewExpression) {
final PsiAnonymousClass anonymousClass = ((PsiNewExpression)context).getAnonymousClass();
if (AnonymousCanBeLambdaInspection.canBeConvertedToLambda(anonymousClass, false)) {
return new TypeConversionDescriptorBase() {
@Override
public PsiExpression replace(PsiExpression expression) throws IncorrectOperationException {
return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, false);
};
};
}
}
return null;
}
private boolean canConvert(PsiType from, PsiType to) {
if (from instanceof PsiEllipsisType) {
from = ((PsiEllipsisType)from).getComponentType();
}
if (to instanceof PsiEllipsisType) {
to = ((PsiEllipsisType)to).getComponentType();
}
if (!(from instanceof PsiClassType)) {
return false;
}
if (!(to instanceof PsiClassType)) {
return false;
}
final PsiClassType.ClassResolveResult fromResolveResult = ((PsiClassType)from).resolveGenerics();
PsiClass fromClass = fromResolveResult.getElement();
if (fromClass instanceof PsiAnonymousClass) {
fromClass = ((PsiAnonymousClass)fromClass).getBaseClassType().resolve();
}
if (fromClass == null || !ruleFromClass().equals(fromClass.getQualifiedName())) {
return false;
}
final PsiClassType.ClassResolveResult toResolveResult = ((PsiClassType)to).resolveGenerics();
final PsiClass toClass = toResolveResult.getElement();
if (toClass == null || !ruleToClass().equals(toClass.getQualifiedName())) {
return false;
}
//final Collection<PsiType> fromTypeParameters = fromResolveResult.getSubstitutor().getSubstitutionMap().values();
//final Collection<PsiType> toTypeParameters = toResolveResult.getSubstitutor().getSubstitutionMap().values();
return true;//!fromTypeParameters.equals(toTypeParameters);
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2000-2015 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.refactoring.typeMigration.rules.guava;
import com.intellij.codeInspection.java18StreamApi.StreamApiConstants;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public class GuavaFluentIterableConversionRule extends BaseGuavaTypeConversionRule {
@Override
protected void fillSimpleDescriptors(Map<String, TypeConversionDescriptorBase> descriptorsMap) {
descriptorsMap.put("contains",
new TypeConversionDescriptor("$it$.contains($o$)", "$it$.anyMatch(e -> e != null && e.equals(%s))"));
descriptorsMap.put("from", new TypeConversionDescriptor("FluentIterable.from($it$)", "$it$.stream()"));
descriptorsMap.put("isEmpty", new TypeConversionDescriptor("$q$.isEmpty()", "$q$.findAny().isPresent()"));
descriptorsMap.put("skip", new TypeConversionDescriptorBase());
descriptorsMap.put("limit", new TypeConversionDescriptorBase());
descriptorsMap.put("transform", new LambdaParametersTypeConversionDescription("$q$.transform($params$)", "$q$.map($params$)"));
descriptorsMap.put("transformAndConcat",
new LambdaParametersTypeConversionDescription("$q$.transformAndConcat($params$)", "$q$.flatMap($params$)"));
descriptorsMap.put("first", new TypeConversionDescriptor("$q$.first()", "$q$.findFirst()"));
descriptorsMap.put("allMatch", new TypeConversionDescriptor("$it$.allMatch($c$)", "$it$." + StreamApiConstants.ALL_MATCH + "($c$)"));
descriptorsMap.put("anyMatch", new TypeConversionDescriptor("$it$.anyMatch($c$)", "$it$." + StreamApiConstants.ANY_MATCH + "($c$)"));
descriptorsMap.put("filter", new TypeConversionDescriptor("$it$.filter($p$)", "$it$." + StreamApiConstants.FILTER + "($p$)"));
descriptorsMap.put("first", new TypeConversionDescriptor("$it$.first()", "$it$." + StreamApiConstants.FIND_FIRST + "()"));
descriptorsMap.put("firstMatch", new TypeConversionDescriptor("$it$.firstMatch($p$)", "$it$.filter($p$).findFirst()"));
descriptorsMap.put("get", new TypeConversionDescriptor("$it$.get($p$)", "$it$.collect(java.util.stream.Collectors.toList()).get($p$)"));
descriptorsMap.put("size", new TypeConversionDescriptor("$it$.size()", "$it$.collect(java.util.stream.Collectors.toList()).size()"));
descriptorsMap.put("toMap", new TypeConversionDescriptor("$it$.toMap($f$)",
"$it$.collect(java.util.stream.Collectors.toMap(java.util.function.Function.identity(), $f$))"));
descriptorsMap.put("toList", new TypeConversionDescriptor("$it$.toList()", "$it$.collect(java.util.stream.Collectors.toList())"));
descriptorsMap.put("toSet", new TypeConversionDescriptor("$it$.toSet()", "$it$.collect(java.util.stream.Collectors.toSet())"));
descriptorsMap.put("toSortedList", new TypeConversionDescriptor("$it$.toSortedList($c$)", "$it$.sorted($c$).collect(java.util.stream.Collectors.toList())"));
descriptorsMap.put("toSortedSet", new TypeConversionDescriptor("$it$.toSortedSet($c$)", "$it$.sorted($c$).collect(java.util.stream.Collectors.toSet())"));
}
@NotNull
@Override
public String ruleFromClass() {
return "com.google.common.collect.FluentIterable";
}
@NotNull
@Override
public String ruleToClass() {
return StreamApiConstants.JAVA_UTIL_STREAM_STREAM;
}
}
@@ -0,0 +1,62 @@
/*
* Copyright 2000-2015 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.refactoring.typeMigration.rules.guava;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiType;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public class GuavaFunctionConversionRule extends BaseGuavaTypeConversionRule {
@Override
protected void fillSimpleDescriptors(Map<String, TypeConversionDescriptorBase> descriptorsMap) {
descriptorsMap.put("apply", new TypeConversionDescriptorBase());
}
@Nullable
@Override
protected TypeConversionDescriptorBase findConversionForMethod(@NotNull PsiType from,
@NotNull PsiType to,
@NotNull PsiMethod method,
String methodName,
PsiExpression context,
TypeMigrationLabeler labeler) {
if (method.isConstructor()) {
System.out.println("123");
}
return null;
}
@NotNull
@Override
public String ruleFromClass() {
return "com.google.common.base.Function";
}
@NotNull
@Override
public String ruleToClass() {
return "java.util.function.Function";
}
}
@@ -0,0 +1,134 @@
/*
* Copyright 2000-2015 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.refactoring.typeMigration.rules.guava;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public class GuavaOptionalConversionRule extends BaseGuavaTypeConversionRule {
public final static String GUAVA_OPTIONAL = "com.google.common.base.Optional";
@Nullable
@Override
protected TypeConversionDescriptorBase findConversionForMethod(@NotNull PsiType from,
@NotNull PsiType to,
@NotNull PsiMethod method,
String methodName,
PsiExpression context,
TypeMigrationLabeler labeler) {
if ("or".equals(methodName)) {
PsiMethodCallExpression methodCallExpression = null;
if (context instanceof PsiMethodCallExpression) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length != 1) {
return null;
}
final PsiClass aClass = PsiTypesUtil.getPsiClass(parameters[0].getType());
if (aClass != null) {
final String qName = aClass.getQualifiedName();
if (GUAVA_OPTIONAL.equals(qName)) {
return new TypeConversionDescriptor("$expr$", "java.util.Optional.ofNullable($expr$.get())");
}
else if ("com.google.common.bas.Supplier".equals(qName)) {
return new TypeConversionDescriptor("$expr$", "java.util.Optional.ofNullable($expr$)");
}
else {
return new TypeConversionDescriptor("$expr$", "java.util.Optional.ofNullable($expr$)");
}
}
return null;
} else if (context.getParent() instanceof PsiMethodCallExpression) {
methodCallExpression = (PsiMethodCallExpression)context.getParent();
}
if (methodCallExpression == null) {
return null;
}
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length != 1) {
return null;
}
final PsiClass aClass = PsiTypesUtil.getPsiClass(parameters[0].getType());
if (aClass != null) {
final String qName = aClass.getQualifiedName();
if (GUAVA_OPTIONAL.equals(qName)) {
return new TypeConversionDescriptor("$val$.or($other$)", "java.util.Optional.ofNullable($val$.orElseGet($other$::get))", to);
}
else if ("com.google.common.bas.Supplier".equals(qName)) {
return new TypeConversionDescriptor("$val$.or($other$)", "$val$.orElseGet($other$::get)");
}
else {
return new TypeConversionDescriptor("$val$.or($other$)", "$val$.orElse($other$)");
}
}
return null;
}
return null;
}
@Override
protected void fillSimpleDescriptors(Map<String, TypeConversionDescriptorBase> descriptorsMap) {
descriptorsMap.put("absent", new TypeConversionDescriptor("Optional.absent()", "java.util.Optional.empty()") {
@Override
public PsiExpression replace(PsiExpression expression) {
assert expression instanceof PsiMethodCallExpression;
final PsiReferenceParameterList typeArguments = ((PsiMethodCallExpression)expression).getTypeArgumentList();
PsiReferenceParameterList typeArgumentsCopy =
typeArguments.getTypeArguments().length == 0 ? null : (PsiReferenceParameterList)typeArguments.copy();
final PsiMethodCallExpression replacedExpression = (PsiMethodCallExpression)super.replace(expression);
if (typeArgumentsCopy != null) {
replacedExpression.getTypeArgumentList().replace(typeArgumentsCopy);
}
return replacedExpression;
}
});
descriptorsMap.put("of", new TypeConversionDescriptor("Optional.of($ref$)", "java.util.Optional.of($ref$)"));
descriptorsMap.put("fromNullable", new TypeConversionDescriptor("Optional.fromNullable($ref$)", "java.util.Optional.ofNullable($ref$)"));
descriptorsMap.put("presentInstances", new TypeConversionDescriptor("Optional.presentInstances($it$)", "java.util.stream.StreamSupport.stream($it$.spliterator(), false).map(java.util.Optional::get).collect(java.util.Collectors.toList())"));
final TypeConversionDescriptorBase identity = new TypeConversionDescriptorBase();
descriptorsMap.put("get", identity);
descriptorsMap.put("isPresent", identity);
descriptorsMap.put("orNull", new TypeConversionDescriptor("$val$.orNull()", "$val$.orElse(null)"));
descriptorsMap.put("asSet", new TypeConversionDescriptor("$val$.asSet()",
"$val$.isPresent() ? java.util.Collections.singleton($val$.get()) : java.util.Collections.emptySet()"));
descriptorsMap.put("transform", new TypeConversionDescriptor("$val$.transform($fun$)", "$val$.map($fun$)"));
}
@NotNull
@Override
public String ruleFromClass() {
return "com.google.common.base.Optional";
}
@NotNull
@Override
public String ruleToClass() {
return "java.util.Optional";
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2000-2015 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.refactoring.typeMigration.rules.guava;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptorBase;
import com.intellij.refactoring.typeMigration.TypeMigrationLabeler;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author Dmitry Batkovich
*/
public class GuavaPredicateConversionRule extends BaseGuavaTypeConversionRule {
@Override
protected void fillSimpleDescriptors(Map<String, TypeConversionDescriptorBase> descriptorsMap) {
descriptorsMap.put("apply", new TypeConversionDescriptor("$q$.apply($o$)", "$q$.test($o$)"));
}
@NotNull
@Override
public String ruleFromClass() {
return "com.google.common.base.Predicate";
}
@NotNull
@Override
public String ruleToClass() {
return "java.util.function.Predicate";
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2000-2015 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.refactoring.typeMigration.rules.guava;
import com.intellij.codeInspection.AnonymousCanBeLambdaInspection;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.refactoring.typeMigration.TypeConversionDescriptor;
import org.jetbrains.annotations.NonNls;
/**
* @author Dmitry Batkovich
*/
public class LambdaParametersTypeConversionDescription extends TypeConversionDescriptor {
private static final Logger LOG = Logger.getInstance(LambdaParametersTypeConversionDescription.class);
public LambdaParametersTypeConversionDescription(@NonNls String stringToReplace, @NonNls String replaceByString) {
super(stringToReplace, replaceByString);
}
@Override
public PsiExpression replace(PsiExpression expression) {
LOG.assertTrue(expression instanceof PsiMethodCallExpression);
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
final PsiExpression[] arguments = methodCall.getArgumentList().getExpressions();
if (arguments.length == 1) {
final PsiExpression functionArg = arguments[0];
convertParameter(functionArg);
}
return super.replace(expression);
}
private static PsiExpression addApplyReference(final PsiExpression expression) {
return (PsiExpression)expression.replace(JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText(expression.getText() + "::apply", null));
}
public static PsiExpression convertParameter(PsiExpression expression) {
if (expression instanceof PsiNewExpression) {
final PsiAnonymousClass anonymousClass = ((PsiNewExpression)expression).getAnonymousClass();
if (anonymousClass != null) {
if (AnonymousCanBeLambdaInspection.canBeConvertedToLambda(anonymousClass, true)) {
AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true);
}
}
else {
return addApplyReference(expression);
}
}
else if (!(expression instanceof PsiFunctionalExpression)) {
return addApplyReference(expression);
}
return expression;
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2000-2015 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.codeInsight.inspections;
import com.intellij.codeInsight.daemon.impl.quickfix.VariableTypeFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.application.PathManager;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.refactoring.typeMigration.inspections.GuavaInspection;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
import java.io.File;
/**
* @author Dmitry Batkovich
*/
public class GuavaInspectionTest extends JavaCodeInsightFixtureTestCase {
@Override
protected String getTestDataPath() {
return PlatformTestUtil.getCommunityPath() + "/java/typeMigration/testData/inspections/guava";
}
@Override
protected void tuneFixture(JavaModuleFixtureBuilder moduleBuilder) throws Exception {
moduleBuilder.setLanguageLevel(LanguageLevel.JDK_1_8);
moduleBuilder.addLibraryJars("guava-17.0.jar", PathManager.getHomePath().replace(File.separatorChar, '/') + "/community/lib/",
"guava-17.0.jar");
moduleBuilder.addLibraryJars("guava-17.0.jar-2", PathManager.getHomePath().replace(File.separatorChar, '/') + "/lib/",
"guava-17.0.jar");
moduleBuilder.addJdk(IdeaTestUtil.getMockJdk18Path().getPath());
}
public void _testOptional() {
doTest();
}
public void _testOptional2() {
doTest();
}
public void _testSimpleFluentIterable() {
doTest();
}
private void doTest() {
myFixture.configureByFile(getTestName(true) + ".java");
myFixture.enableInspections(new GuavaInspection());
boolean actionFound = false;
myFixture.doHighlighting();
for (IntentionAction action : myFixture.getAvailableIntentions()) {
if (action instanceof VariableTypeFix) {
myFixture.launchAction(action);
actionFound = true;
break;
}
}
assertTrue(actionFound);
myFixture.checkResultByFile(getTestName(true) + "_after.java");
}
}
@@ -0,0 +1,17 @@
import com.google.common.base.Optional;
class A {
void m() {
Option<caret>al<String> o = Optional.absent();
String s = o.get();
Optional<String> yetAnotherOptional = Optional.fromNullable("bla-bla");
System.out.println(o.or(yetAnotherOptional));
if (o.isPresent()) {
System.out.println(o.get());
}
System.out.println(o.orNull());
Set<String> set = o.asSet();
}
}
@@ -0,0 +1,17 @@
import com.google.common.base.Optional;
class A {
Optional<String> m1() {
Optiona<caret>l<String> o = Optional.absent();
String s = o.get();
return o;
}
void w1() {
Optional<String> someVar = m1();
System.out.println(someVar.get());
System.out.println(someVar.isPresent() ? Collections.singleton(someVar.get()) : Collections.emptySet());
}
}
@@ -0,0 +1,19 @@
import com.google.common.base.Optional;
import java.util.Collections;
class A {
void m() {
java.util.Optional<String> o = java.util.Optional.empty();
String s = o.get();
Optional<String> yetAnotherOptional = Optional.fromNullable("bla-bla");
System.out.println(java.util.Optional.ofNullable(o.orElseGet(yetAnotherOptional::get)));
if (o.isPresent()) {
System.out.println(o.get());
}
System.out.println(o.orElse(null));
Set<String> set = o.isPresent() ? Collections.singleton(o.get()) : Collections.emptySet();
}
}
@@ -0,0 +1,32 @@
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import java.util.ArrayList;
import java.util.List;
class A {
Function<String, String> myFunction = new Function<String, String>() {
@Override
public String apply(String input) {
return input.trim();
}
};
List<String> main(String[] args) {
ArrayList<String> strings = new ArrayList<String>();
FluentIterable<String> it = FluentIterable.from(strings);
it = it.transform(input -> input.intern());
it = it.transform(String::trim);
it = it.transform(new Function<String, String>() {
@Override
public String apply(String input) {
System.out.println("do some action on " + input);
return input.substring(0, 10);
}
});
it = it.transform(myFunction);
return it.toList();
}
}
@@ -0,0 +1,30 @@
import com.google.common.base.Function;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class A {
Function<String, String> myFunction = new Function<String, String>() {
@Override
public String apply(String input) {
return input.trim();
}
};
List<String> main(String[] args) {
ArrayList<String> strings = new ArrayList<String>();
Stream<String> it = strings.stream();
it = it.map(input -> input.intern());
it = it.map(String::trim);
it = it.map(input -> {
System.out.println("do some action on " + input);
return input.substring(0, 10);
});
it = it.map(myFunction::apply);
return it.collect(Collectors.toList());
}
}
@@ -0,0 +1,6 @@
<html>
<body>
Inspection detects usages of Guava's functional primitives like
<b>c.g.c.c.FluentIterable</b>, <b>c.g.c.b.Optional</b>, <b>c.g.c.b.Function</b> and <b>c.g.c.b.Supplier</b>.
</body>
</html>