diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsFix.java
index b0a80a0a7fa9..7ee7ea1c245d 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsFix.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddTypeArgumentsFix.java
@@ -25,6 +25,7 @@ import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
public class AddTypeArgumentsFix extends MethodArgumentFix {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.AddTypeArgumentsFix");
@@ -51,57 +52,7 @@ public class AddTypeArgumentsFix extends MethodArgumentFix {
@Override
protected PsiExpression getModifiedArgument(final PsiExpression expression, final PsiType toType) throws IncorrectOperationException {
- if (!PsiUtil.isLanguageLevel5OrHigher(expression)) return null;
-
- if (expression instanceof PsiMethodCallExpression) {
- final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
- final PsiReferenceParameterList list = methodCall.getMethodExpression().getParameterList();
- if (list == null || list.getTypeArguments().length > 0) return null;
- final JavaResolveResult resolveResult = methodCall.resolveMethodGenerics();
- final PsiElement element = resolveResult.getElement();
- if (element instanceof PsiMethod) {
- final PsiMethod method = (PsiMethod)element;
- final PsiType returnType = method.getReturnType();
- if (returnType == null) return null;
-
- final PsiTypeParameter[] typeParameters = method.getTypeParameters();
- if (typeParameters.length > 0) {
- PsiType[] mappings = PsiType.createArray(typeParameters.length);
- PsiResolveHelper helper = JavaPsiFacade.getInstance(expression.getProject()).getResolveHelper();
- LanguageLevel level = PsiUtil.getLanguageLevel(expression);
- for (int i = 0; i < typeParameters.length; i++) {
- PsiTypeParameter typeParameter = typeParameters[i];
- final PsiType substitution = helper.getSubstitutionForTypeParameter(typeParameter, returnType, toType, false, level);
- if (substitution == null || PsiType.NULL.equals(substitution)) return null;
- mappings[i] = GenericsUtil.eliminateWildcards(substitution, false);
- }
-
- final PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory();
- PsiMethodCallExpression copy = (PsiMethodCallExpression)expression.copy();
- final PsiReferenceExpression methodExpression = copy.getMethodExpression();
- final PsiReferenceParameterList parameterList = methodExpression.getParameterList();
- LOG.assertTrue(parameterList != null);
- for (PsiType mapping : mappings) {
- parameterList.add(factory.createTypeElement(mapping));
- }
- if (methodExpression.getQualifierExpression() == null) {
- final PsiExpression qualifierExpression;
- final PsiClass containingClass = method.getContainingClass();
- LOG.assertTrue(containingClass != null);
- if (method.hasModifierProperty(PsiModifier.STATIC)) {
- qualifierExpression = factory.createReferenceExpression(containingClass);
- } else {
- qualifierExpression = RefactoringChangeUtil.createThisExpression(method.getManager(), null);
- }
- methodExpression.setQualifierExpression(qualifierExpression);
- }
-
- return (PsiExpression)JavaCodeStyleManager.getInstance(copy.getProject()).shortenClassReferences(copy);
- }
- }
- }
-
- return null;
+ return addTypeArguments(expression, toType);
}
@Override
@@ -110,5 +61,59 @@ public class AddTypeArgumentsFix extends MethodArgumentFix {
}
}
+ @Nullable
+ public static PsiExpression addTypeArguments(PsiExpression expression, PsiType toType) {
+ if (!PsiUtil.isLanguageLevel5OrHigher(expression)) return null;
+
+ if (expression instanceof PsiMethodCallExpression) {
+ final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)expression;
+ final PsiReferenceParameterList list = methodCall.getMethodExpression().getParameterList();
+ if (list == null || list.getTypeArguments().length > 0) return null;
+ final JavaResolveResult resolveResult = methodCall.resolveMethodGenerics();
+ final PsiElement element = resolveResult.getElement();
+ if (element instanceof PsiMethod) {
+ final PsiMethod method = (PsiMethod)element;
+ final PsiType returnType = method.getReturnType();
+ if (returnType == null) return null;
+
+ final PsiTypeParameter[] typeParameters = method.getTypeParameters();
+ if (typeParameters.length > 0) {
+ PsiType[] mappings = PsiType.createArray(typeParameters.length);
+ PsiResolveHelper helper = JavaPsiFacade.getInstance(expression.getProject()).getResolveHelper();
+ LanguageLevel level = PsiUtil.getLanguageLevel(expression);
+ for (int i = 0; i < typeParameters.length; i++) {
+ PsiTypeParameter typeParameter = typeParameters[i];
+ final PsiType substitution = helper.getSubstitutionForTypeParameter(typeParameter, returnType, toType, false, level);
+ if (substitution == null || PsiType.NULL.equals(substitution)) return null;
+ mappings[i] = GenericsUtil.eliminateWildcards(substitution, false);
+ }
+
+ final PsiElementFactory factory = JavaPsiFacade.getInstance(expression.getProject()).getElementFactory();
+ PsiMethodCallExpression copy = (PsiMethodCallExpression)expression.copy();
+ final PsiReferenceExpression methodExpression = copy.getMethodExpression();
+ final PsiReferenceParameterList parameterList = methodExpression.getParameterList();
+ LOG.assertTrue(parameterList != null);
+ for (PsiType mapping : mappings) {
+ parameterList.add(factory.createTypeElement(mapping));
+ }
+ if (methodExpression.getQualifierExpression() == null) {
+ final PsiExpression qualifierExpression;
+ final PsiClass containingClass = method.getContainingClass();
+ LOG.assertTrue(containingClass != null);
+ if (method.hasModifierProperty(PsiModifier.STATIC)) {
+ qualifierExpression = factory.createReferenceExpression(containingClass);
+ } else {
+ qualifierExpression = RefactoringChangeUtil.createThisExpression(method.getManager(), null);
+ }
+ methodExpression.setQualifierExpression(qualifierExpression);
+ }
+
+ return (PsiExpression)JavaCodeStyleManager.getInstance(copy.getProject()).shortenClassReferences(copy);
+ }
+ }
+ }
+ return null;
+ }
+
public static ArgumentFixerActionFactory REGISTRAR = new AddTypeArgumentsFix.MyFixerActionFactory();
}
diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.form b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.form
index 6c448f4a4e8d..fdd485c53036 100644
--- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.form
+++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.form
@@ -1,6 +1,6 @@
diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.java
index 8a8461045f36..d84d67e879f4 100644
--- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.java
+++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/AddMethodsDialog.java
@@ -16,27 +16,36 @@
package com.intellij.codeInspection.java18StreamApi;
+import com.intellij.codeInsight.intention.impl.config.ActionUsagePanel;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.event.DocumentAdapter;
import com.intellij.openapi.editor.event.DocumentEvent;
+import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.ui.DialogWrapper;
-import com.intellij.psi.JavaPsiFacade;
-import com.intellij.psi.PsiClass;
-import com.intellij.psi.PsiMethod;
-import com.intellij.psi.PsiModifier;
+import com.intellij.openapi.util.Condition;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.refactoring.ui.ClassNameReferenceEditor;
+import com.intellij.ui.CollectionComboBoxModel;
import com.intellij.ui.ColoredListCellRenderer;
+import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.*;
+import java.util.List;
/**
* @author Dmitry Batkovich
@@ -46,36 +55,94 @@ public class AddMethodsDialog extends DialogWrapper {
@NotNull private final Project myProject;
private JPanel myPanel;
- private ComboBox myPatternsCombo;
+ private ComboBox myTemplatesCombo;
private ClassNameReferenceEditor myClassNameEditor;
private ComboBox myMethodNameCombo;
+ private ActionUsagePanel myBeforeActionPanel;
+ private ActionUsagePanel myAfterActionPanel;
+ private JPanel myExamplePanel;
@SuppressWarnings("unchecked")
protected AddMethodsDialog(@NotNull final Project project, @NotNull final Component parent, boolean canBeParent) {
super(parent, canBeParent);
myProject = project;
- final DefaultComboBoxModel model = new DefaultComboBoxModel();
- myPatternsCombo.setModel(model);
- for (String methodName : StreamApiConstants.STREAM_STREAM_API_METHODS.getValue()) {
- model.addElement(methodName);
- }
- model.addElement(StreamApiConstants.FAKE_FIND_MATCHED);
- myPatternsCombo.setRenderer(new ColoredListCellRenderer() {
+ myTemplatesCombo.setEnabled(false);
+ myTemplatesCombo.setRenderer(new ColoredListCellRenderer() {
@Override
- protected void customizeCellRenderer(JList list, String methodName, int index, boolean selected, boolean hasFocus) {
+ protected void customizeCellRenderer(JList list,
+ PseudoLambdaReplaceTemplate template,
+ int index,
+ boolean selected,
+ boolean hasFocus) {
+ if (template == null) {
+ return;
+ }
append("stream.");
- if (StreamApiConstants.STREAM_STREAM_API_METHODS.getValue().contains(methodName)) {
- append(methodName + "()", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
+ final String streamApiMethodName = template.getStreamApiMethodName();
+ if (StreamApiConstants.STREAM_STREAM_API_METHODS.getValue().contains(streamApiMethodName)) {
+ append(streamApiMethodName + "()", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
}
else {
- LOG.assertTrue(StreamApiConstants.FAKE_FIND_MATCHED.equals(methodName));
+ LOG.assertTrue(StreamApiConstants.FAKE_FIND_MATCHED.equals(streamApiMethodName));
append(String.format(StreamApiConstants.FAKE_FIND_MATCHED_PATTERN, "condition"), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
append(" or ");
- append(String.format(StreamApiConstants.FAKE_FIND_MATCHED_WITH_DEFAULT_PATTERN, "condition", "defaultValue"), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
+ append(".orElseGet(() -> defaultValue)", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
+ }
+ }
+ });
+ myTemplatesCombo.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ final PseudoLambdaReplaceTemplate template = (PseudoLambdaReplaceTemplate)e.getItem();
+ final Collection methods = (Collection)myMethodNameCombo.getSelectedItem();
+ if (methods == null) {
+ return;
+ }
+ for (PsiMethod method : methods) {
+ if (template.validate(method) != null) {
+ showTemplateExample(template, method);
+ break;
+ }
}
}
});
myMethodNameCombo.setModel(new DefaultComboBoxModel());
+ myMethodNameCombo.addItemListener(new ItemListener() {
+ @Override
+ public void itemStateChanged(ItemEvent e) {
+ if (!myExamplePanel.isEnabled()) {
+ myExamplePanel.setEnabled(true);
+ }
+ final Collection suitableTemplates = new LinkedHashSet();
+ final Collection methods = (Collection) e.getItem();
+ for (PseudoLambdaReplaceTemplate template : PseudoLambdaReplaceTemplate.getAllTemplates()) {
+ for (PsiMethod method : methods) {
+ if (template.validate(method) != null) {
+ if (suitableTemplates.isEmpty()) {
+ showTemplateExample(template, method);
+ }
+ suitableTemplates.add(template);
+ }
+ }
+ }
+ if (!myTemplatesCombo.isEnabled()) {
+ myTemplatesCombo.setEnabled(true);
+ }
+ LOG.assertTrue(!suitableTemplates.isEmpty());
+ final List templatesAsList = new ArrayList(suitableTemplates);
+ myTemplatesCombo.setModel(new CollectionComboBoxModel(templatesAsList));
+ myTemplatesCombo.setSelectedItem(templatesAsList.get(0));
+ }
+ });
+ myMethodNameCombo.setRenderer(new ListCellRendererWrapper>() {
+ @Override
+ public void customize(JList list, Collection methods, int index, boolean selected, boolean hasFocus) {
+ if (methods != null) {
+ LOG.assertTrue(!methods.isEmpty());
+ setText(ContainerUtil.getFirstItem(methods).getName());
+ }
+ }
+ });
myClassNameEditor.addDocumentListener(new DocumentAdapter() {
@Override
public void documentChanged(DocumentEvent e) {
@@ -87,34 +154,79 @@ public class AddMethodsDialog extends DialogWrapper {
myMethodNameCombo.setEnabled(false);
}
else {
- for (String name : ContainerUtil.newTreeSet(ContainerUtil.mapNotNull(aClass.getMethods(), new Function() {
+ final List possibleTemplates = PseudoLambdaReplaceTemplate.getAllTemplates();
+ final MultiMap nameToMethod = new MultiMap();
+ for (PsiMethod m : ContainerUtil.filter(aClass.getMethods(), new Condition() {
@Override
- public String fun(PsiMethod method) {
+ public boolean value(PsiMethod method) {
if (method.isConstructor() ||
!method.hasModifierProperty(PsiModifier.STATIC) ||
method.hasModifierProperty(PsiModifier.PRIVATE)) {
- return null;
+ return false;
}
- return method.getName();
+ boolean templateFound = false;
+ for (PseudoLambdaReplaceTemplate template : possibleTemplates) {
+ if (template.validate(method) != null) {
+ templateFound = true;
+ }
+ }
+ if (!templateFound) {
+ return false;
+ }
+ return true;
}
- }))) {
- comboBoxModel.addElement(name);
+ })) {
+ nameToMethod.putValue(m.getName(), m);
+ }
+ for (Map.Entry> entry : nameToMethod.entrySet()) {
+ comboBoxModel.addElement(entry.getValue());
}
myMethodNameCombo.setEnabled(true);
}
}
});
+
+ myExamplePanel.setEnabled(false);
init();
}
+ private void showTemplateExample(final PseudoLambdaReplaceTemplate template, final PsiMethod method) {
+ final PsiClass aClass = method.getContainingClass();
+ LOG.assertTrue(aClass != null);
+ final String fqn = aClass.getQualifiedName();
+ LOG.assertTrue(fqn != null);
+ final String parameters =
+ StringUtil.join(ContainerUtil.map(method.getParameterList().getParameters(), new Function() {
+ @Override
+ public String fun(PsiParameter parameter) {
+ return parameter.getName();
+ }
+ }), ", ");
+ final String expressionText = fqn + "." + method.getName() + "(" + parameters + ")";
+ final PsiExpression psiExpression = JavaPsiFacade.getElementFactory(method.getProject())
+ .createExpressionFromText(expressionText, null);
+ LOG.assertTrue(psiExpression instanceof PsiMethodCallExpression);
+ final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)psiExpression;
+ template.convertToStream(methodCallExpression, method, false);
+ myBeforeActionPanel.reset("void example() {\n " + methodCallExpression.getText() + ";\n}", StdFileTypes.JAVA);
+ myAfterActionPanel.reset("void example() {\n " + template.convertToStream(methodCallExpression, method, true).getText() + "\n}", StdFileTypes.JAVA);
+ }
+
+ @Override
+ protected void dispose() {
+ Disposer.dispose(myBeforeActionPanel);
+ Disposer.dispose(myAfterActionPanel);
+ super.dispose();
+ }
+
private void createUIComponents() {
myClassNameEditor = new ClassNameReferenceEditor(myProject, null);
}
public StaticPseudoFunctionalStyleMethodOptions.PipelineElement getSelectedElement() {
return new StaticPseudoFunctionalStyleMethodOptions.PipelineElement(myClassNameEditor.getText(),
- (String)myMethodNameCombo.getSelectedItem(),
- (String)myPatternsCombo.getSelectedItem());
+ ContainerUtil.getFirstItem((Collection < PsiMethod >)myMethodNameCombo.getSelectedItem()).getName(),
+ (PseudoLambdaReplaceTemplate)myTemplatesCombo.getSelectedItem());
}
@Nullable
diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java
new file mode 100644
index 000000000000..4c425ed5d9d9
--- /dev/null
+++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/PseudoLambdaReplaceTemplate.java
@@ -0,0 +1,629 @@
+/*
+ * 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.codeInspection.java18StreamApi;
+
+import com.intellij.codeInsight.daemon.impl.quickfix.AddTypeArgumentsFix;
+import com.intellij.codeInspection.AnonymousCanBeLambdaInspection;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Condition;
+import com.intellij.psi.*;
+import com.intellij.psi.codeStyle.JavaCodeStyleManager;
+import com.intellij.psi.impl.PsiDiamondTypeUtil;
+import com.intellij.psi.impl.PsiSubstitutorImpl;
+import com.intellij.psi.impl.source.PsiClassReferenceType;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.psi.util.*;
+import com.intellij.util.Function;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.containers.hash.HashMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * @author Dmitry Batkovich
+ */
+class PseudoLambdaReplaceTemplate {
+ private final static Logger LOG = Logger.getInstance(PseudoLambdaReplaceTemplate.class);
+
+ public enum LambdaRole {
+ PREDICATE,
+ FUNCTION
+ }
+
+ public static class ValidationInfo {
+ private final int myLambdaPosition;
+ private final int myIterablePosition;
+ private final int myDefaultValuePosition;
+
+ public ValidationInfo(int lambdaPosition, int iterablePosition, int defaultValuePosition) {
+ myLambdaPosition = lambdaPosition;
+ myIterablePosition = iterablePosition;
+ myDefaultValuePosition = defaultValuePosition;
+ }
+
+ public int getLambdaPosition() {
+ return myLambdaPosition;
+ }
+
+ public int getIterablePosition() {
+ return myIterablePosition;
+ }
+
+ public int getDefaultValuePosition() {
+ return myDefaultValuePosition;
+ }
+ }
+
+ static final PseudoLambdaReplaceTemplate MAP =
+ new PseudoLambdaReplaceTemplate(StreamApiConstants.MAP, LambdaRole.FUNCTION, false);
+ static final PseudoLambdaReplaceTemplate FILTER =
+ new PseudoLambdaReplaceTemplate(StreamApiConstants.FILTER, LambdaRole.PREDICATE, false);
+ static final PseudoLambdaReplaceTemplate FIND =
+ new PseudoLambdaReplaceTemplate(StreamApiConstants.FAKE_FIND_MATCHED, LambdaRole.PREDICATE, true);
+ static final PseudoLambdaReplaceTemplate ALL_MATCH =
+ new PseudoLambdaReplaceTemplate(StreamApiConstants.ALL_MATCH, LambdaRole.PREDICATE, false);
+ static final PseudoLambdaReplaceTemplate ANY_MATCH =
+ new PseudoLambdaReplaceTemplate(StreamApiConstants.ANY_MATCH, LambdaRole.PREDICATE, false);
+
+ private final String myStreamApiMethodName;
+ private final LambdaRole myLambdaRole;
+ private final boolean myAcceptDefaultValue;
+
+ public PseudoLambdaReplaceTemplate(String method,
+ LambdaRole type,
+ boolean acceptDefaultValue) {
+ myStreamApiMethodName = method;
+ myLambdaRole = type;
+ myAcceptDefaultValue = acceptDefaultValue;
+ }
+
+ public static List getAllTemplates() {
+ return ContainerUtil.newArrayList(MAP, FILTER, FIND, ALL_MATCH, ANY_MATCH);
+ }
+
+ public ValidationInfo validate(final PsiMethod method) {
+ final PsiParameter[] parameters = method.getParameterList().getParameters();
+ final PsiType[] parameterTypes =
+ ContainerUtil.map2Array(Arrays.asList(parameters), new PsiType[parameters.length], new Function() {
+ @Override
+ public PsiType fun(PsiParameter parameter) {
+ return parameter.getType();
+ }
+ });
+ final PsiType returnType = method.getReturnType();
+
+ if (returnType instanceof PsiClassType) {
+ final PsiClass resolvedReturnTypeClass = ((PsiClassType)returnType).resolve();
+ if (!InheritanceUtil.isInheritor(resolvedReturnTypeClass, CommonClassNames.JAVA_LANG_ITERABLE)) {
+ return null;
+ }
+ } else if (!(returnType instanceof PsiArrayType)) {
+ return null;
+ }
+ return validate(parameterTypes, returnType, method);
+ }
+
+ @Nullable
+ public ValidationInfo validate(final PsiMethodCallExpression expression) {
+ final PsiType[] argumentTypes = expression.getArgumentList().getExpressionTypes();
+ final PsiType methodReturnType = expression.getType();
+ if (methodReturnType == null) {
+ return null;
+ }
+
+ final PsiMethod method = expression.resolveMethod();
+ if (method == null) {
+ return null;
+ }
+ final PsiParameter[] expectedParameters = method.getParameterList().getParameters();
+
+ if (argumentTypes.length != expectedParameters.length) {
+ return null;
+ }
+ return validate(argumentTypes, methodReturnType, expression);
+ }
+
+ public String getStreamApiMethodName() {
+ return myStreamApiMethodName;
+ }
+
+ public LambdaRole getLambdaRole() {
+ return myLambdaRole;
+ }
+
+ public boolean isAcceptDefaultValue() {
+ return myAcceptDefaultValue;
+ }
+
+ private ValidationInfo validate(final PsiType[] arguments, final PsiType methodReturnType, final PsiElement context) {
+ int lambdaPosition = -1;
+ int defaultValuePosition = -1;
+ int iterablePosition = -1;
+
+ if (!myAcceptDefaultValue) {
+ if (arguments.length != 2) {
+ return null;
+ }
+ } else {
+ if (arguments.length != 2 && arguments.length != 3) {
+ return null;
+ }
+ }
+
+ for (int i = 0; i < arguments.length; i++) {
+ PsiType type = arguments[i];
+ if (isFunction(type, methodReturnType, context)) {
+ if (lambdaPosition == -1) {
+ lambdaPosition = i;
+ continue;
+ }
+ else {
+ return null;
+ }
+ }
+ if (isIterableOrArray(type)) {
+ if (iterablePosition == -1) {
+ iterablePosition = i;
+ continue;
+ }
+ else {
+ return null;
+ }
+ }
+ if (myAcceptDefaultValue && methodReturnType.isAssignableFrom(type)) {
+ if (defaultValuePosition == -1) {
+ defaultValuePosition = i;
+ }
+ else {
+ return null;
+ }
+ }
+ }
+
+ if (lambdaPosition == -1 || iterablePosition == -1) {
+ return null;
+ }
+ if (myAcceptDefaultValue) {
+ if (defaultValuePosition == -1 && arguments.length == 3) {
+ return null;
+ }
+ }
+ return new ValidationInfo(lambdaPosition, iterablePosition, defaultValuePosition);
+ }
+
+ private boolean isFunction(PsiType type, PsiType baseMethodReturnType, PsiElement context) {
+ if (type instanceof PsiMethodReferenceType) {
+ final PsiMethodReferenceExpression expression = ((PsiMethodReferenceType)type).getExpression();
+ final PsiMethod resolvedMethod = (PsiMethod)expression.resolve();
+ if (resolvedMethod == null) {
+ return false;
+ }
+ final PsiParameter[] parameters = resolvedMethod.getParameterList().getParameters();
+ if ((parameters.length != 1 && myLambdaRole == LambdaRole.FUNCTION) || (parameters.length != 0 && myLambdaRole == LambdaRole.PREDICATE)) {
+ return false;
+ }
+ final PsiType returnType = LambdaUtil.getFunctionalInterfaceReturnType(expression);
+ return isSuitableLambdaRole(returnType, baseMethodReturnType, context);
+ } else if (type instanceof PsiLambdaExpressionType) {
+ final PsiLambdaExpression expression = ((PsiLambdaExpressionType)type).getExpression();
+ final PsiType psiType = LambdaUtil.getFunctionalInterfaceReturnType(expression.getFunctionalInterfaceType());
+ return isSuitableLambdaRole(psiType, baseMethodReturnType, context);
+ } else if (isSuitableFunctionalType(type, baseMethodReturnType, context)) {
+ return true;
+ }
+ return isJavaLangClassType(type) && myLambdaRole == LambdaRole.PREDICATE;
+ }
+
+ private boolean isSuitableLambdaRole(final PsiType lambdaReturnType,
+ final PsiType baseMethodReturnType,
+ final PsiElement context) {
+ if (lambdaReturnType == null) {
+ return false;
+ }
+ if (myLambdaRole == LambdaRole.PREDICATE) {
+ final PsiClassType boxedBoolean = PsiType.BOOLEAN.getBoxedType(context);
+ if (!(PsiType.BOOLEAN.equals(lambdaReturnType) || (boxedBoolean != null && boxedBoolean.equals(lambdaReturnType)))) {
+ return false;
+ }
+ }
+ else {
+ LOG.assertTrue(myLambdaRole == LambdaRole.FUNCTION);
+ if (baseMethodReturnType instanceof PsiClassType) {
+ final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)baseMethodReturnType).resolveGenerics();
+ final Map substitutionMap = resolveResult.getSubstitutor().getSubstitutionMap();
+ if (substitutionMap.size() != 1) {
+ return false;
+ }
+ final PsiType iterableParametrizedType = ContainerUtil.getFirstItem(substitutionMap.values());
+ if (!lambdaReturnType.equals(iterableParametrizedType)) {
+ return false;
+ }
+ }
+ else if (baseMethodReturnType instanceof PsiArrayType) {
+ if (!lambdaReturnType.equals(((PsiArrayType)baseMethodReturnType).getComponentType())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private static boolean isJavaLangClassType(PsiType type) {
+ if (type instanceof PsiClassType) {
+ final PsiClass resolvedClass = ((PsiClassType)type).resolve();
+ if (resolvedClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(resolvedClass.getQualifiedName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSuitableFunctionalType(final PsiType type, final PsiType baseMethodReturnType, final PsiElement context) {
+ if (type instanceof PsiClassType) {
+ PsiClass targetClass = ((PsiClassType)type).resolve();
+ if (targetClass != null) {
+ if (targetClass instanceof PsiAnonymousClass) {
+ targetClass = ((PsiAnonymousClass)targetClass).getBaseClassType().resolve();
+ if (targetClass == null) {
+ return false;
+ }
+ }
+ if (!LambdaUtil.isFunctionalClass(targetClass)) {
+ return false;
+ }
+ PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(targetClass);
+ final PsiMethod[] methods = ((PsiClassType)type).resolve().findMethodsByName(method.getName(), false);
+ if (methods.length != 1) {
+ return false;
+ }
+ method = methods[0];
+ if (method == null) {
+ return false;
+ }
+ final PsiType psiType;
+ if (type instanceof PsiClassReferenceType) {
+ final PsiJavaCodeReferenceElement reference = ((PsiClassReferenceType)type).getReference();
+ final PsiClass resolvedClass = ((PsiClassReferenceType)type).resolve();
+ final PsiTypeParameter[] typeParameters = resolvedClass.getTypeParameters();
+ final PsiType[] substitutedTypeParameters = reference.getTypeParameters();
+
+ LOG.assertTrue(typeParameters.length == substitutedTypeParameters.length);
+ final Map substitutionMap = new HashMap();
+ for (int i = 0; i < typeParameters.length; i++) {
+ PsiTypeParameter parameter = typeParameters[i];
+ PsiType t = substitutedTypeParameters[i];
+ substitutionMap.put(parameter, t);
+ }
+ PsiSubstitutor substitutor = PsiSubstitutorImpl.createSubstitutor(substitutionMap);
+ psiType = substitutor.substitute(method.getReturnType());
+ } else {
+ psiType = method.getReturnType();
+ }
+ return isSuitableLambdaRole(psiType, baseMethodReturnType, context);
+ }
+ return false;
+ } else {
+ return false;
+ }
+ }
+
+ @NotNull
+ public PsiExpression convertToStream(final PsiMethodCallExpression expression, PsiMethod method, boolean force) {
+ LOG.assertTrue(expression != null);
+ if (method == null) {
+ method = expression.resolveMethod();
+ if (method == null) {
+ return expression;
+ }
+ }
+ final ValidationInfo validationInfo = force ? validate(method) : validate(expression);
+ if (validationInfo == null) {
+ return expression;
+ }
+ final Project project = expression.getProject();
+ int lambdaIndex = validationInfo.getLambdaPosition();
+
+ final PsiExpression[] expressions = expression.getArgumentList().getExpressions();
+ PsiExpression lambdaExpression = expressions[lambdaIndex];
+ if (!force) {
+ lambdaExpression = convertClassTypeExpression(lambdaExpression);
+ lambdaExpression = convertToJavaLambda(lambdaExpression, myStreamApiMethodName);
+ }
+ LOG.assertTrue(lambdaExpression != null);
+
+ final PsiExpression iterableExpression = expressions[validationInfo.getIterablePosition()];
+ final String pipelineHead = createPipelineHeadText(iterableExpression, lambdaExpression, force);
+
+
+ final String lambdaExpressionText;
+ final String elementText;
+ if (!StreamApiConstants.FAKE_FIND_MATCHED.equals(myStreamApiMethodName)) {
+ elementText = myStreamApiMethodName;
+ lambdaExpressionText = lambdaExpression.getText();
+ }
+ else {
+ elementText = validationInfo.getDefaultValuePosition() != -1
+ ? String.format(StreamApiConstants.FAKE_FIND_MATCHED_WITH_DEFAULT_PATTERN, lambdaExpression.getText(),
+ expressions[validationInfo.getDefaultValuePosition()].getText())
+ : String.format(StreamApiConstants.FAKE_FIND_MATCHED_PATTERN, lambdaExpression.getText());
+ lambdaExpressionText = null;
+ }
+ final String pipelineTail =
+ StreamApiConstants.STREAM_STREAM_API_METHODS.getValue().contains(myStreamApiMethodName)
+ ? findSuitableTailMethodForCollection(method)
+ : null;
+
+ final PsiElement replaced =
+ expression.replace(createPipelineExpression(pipelineHead, elementText, lambdaExpressionText, pipelineTail, project));
+ JavaCodeStyleManager.getInstance(project).shortenClassReferences(replaced.getParent());
+ return (PsiExpression)replaced;
+ }
+
+ private static PsiExpression createPipelineExpression(String pipelineHead,
+ String elementText,
+ String lambdaExpression,
+ String pipelineTail,
+ Project project) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(pipelineHead).append(".").append(elementText);
+ if (lambdaExpression != null) {
+ sb.append("(").append(lambdaExpression).append(")");
+ }
+ if (pipelineTail != null) {
+ sb.append(".").append(pipelineTail);
+ }
+ return JavaPsiFacade.getElementFactory(project).createExpressionFromText(sb.toString(), null);
+ }
+
+ private static void convertNewExpression(PsiMethod containingMethod, PsiNewExpression newExpression, PsiClass expectedReturnClass) {
+ final String expectedReturnQName = expectedReturnClass.getQualifiedName();
+ LOG.assertTrue(expectedReturnQName != null);
+ final PsiElementFactory factory = JavaPsiFacade.getElementFactory(newExpression.getProject());
+ PsiAnonymousClass anonymousClass = PsiTreeUtil.findChildOfType(newExpression, PsiAnonymousClass.class);
+ LOG.assertTrue(anonymousClass != null);
+ PsiJavaCodeReferenceElement referenceElement = PsiTreeUtil.findChildOfType(anonymousClass, PsiJavaCodeReferenceElement.class);
+ LOG.assertTrue(referenceElement != null);
+ final PsiReferenceParameterList parameterList = PsiTreeUtil.findChildOfType(referenceElement, PsiReferenceParameterList.class);
+ final PsiJavaCodeReferenceElement newCodeReferenceElement = factory.createReferenceFromText(expectedReturnClass.getQualifiedName()
+ +
+ (parameterList == null
+ ? ""
+ : parameterList.getText()), null);
+ referenceElement.replace(newCodeReferenceElement);
+ final List methods = ContainerUtil.filter(anonymousClass.getMethods(), new Condition() {
+ @Override
+ public boolean value(PsiMethod method) {
+ return !"equals".equals(method.getName());
+ }
+ });
+ LOG.assertTrue(methods.size() == 1, methods);
+ final PsiMethod method = methods.get(0);
+ method.setName(expectedReturnClass.getMethods()[0].getName());
+ final PsiTypeElement element = containingMethod.getReturnTypeElement();
+ if (element != null) {
+ final PsiReferenceParameterList genericParameter = PsiTreeUtil.findChildOfType(element, PsiReferenceParameterList.class);
+ element.replace(
+ factory.createTypeElementFromText(expectedReturnQName + (genericParameter == null ? "" : genericParameter.getText()), null));
+ }
+ }
+
+ @Nullable
+ private static String findSuitableTailMethodForCollection(PsiMethod lambdaHandler) {
+ final PsiType type = lambdaHandler.getReturnType();
+ if (type instanceof PsiArrayType) {
+ final PsiType arrayComponentType = ((PsiArrayType)type).getComponentType();
+ return "toArray(" + arrayComponentType.getCanonicalText() + "[]::new)";
+ }
+ else if (type instanceof PsiClassType) {
+ final PsiClass resolvedClass = ((PsiClassType)type).resolve();
+ if (resolvedClass == null) {
+ return null;
+ }
+ final String qName = resolvedClass.getQualifiedName();
+ if (qName == null) {
+ return null;
+ }
+ if (qName.equals(CommonClassNames.JAVA_UTIL_LIST)
+ || qName.equals(CommonClassNames.JAVA_UTIL_COLLECTION)
+ || qName.equals(CommonClassNames.JAVA_LANG_ITERABLE)) {
+ return "collect(" + StreamApiConstants.JAVA_UTIL_STREAM_COLLECTORS + ".toList())";
+ }
+ else if (qName.equals(CommonClassNames.JAVA_UTIL_SET)) {
+ return "collect(" + StreamApiConstants.JAVA_UTIL_STREAM_COLLECTORS + ".toSet())";
+ }
+ else if (qName.equals(CommonClassNames.JAVA_UTIL_ITERATOR)) {
+ return "iterator()";
+ }
+ }
+ return null;
+ }
+
+ private static PsiExpression convertToJavaLambda(PsiExpression expression, String streamApiMethodName) {
+ if (streamApiMethodName.equals(StreamApiConstants.FAKE_FIND_MATCHED)) {
+ streamApiMethodName = StreamApiConstants.FILTER;
+ }
+ if (expression instanceof PsiMethodReferenceExpression) {
+ return expression;
+ }
+ if (expression instanceof PsiLambdaExpression) {
+ return expression;
+ }
+ if (expression instanceof PsiMethodCallExpression) {
+ final PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod();
+ if (method == null) {
+ return null;
+ }
+ final PsiType type = method.getReturnType();
+ if (!(type instanceof PsiClassType)) {
+ return null;
+ }
+ final PsiClass lambdaClass = ((PsiClassType)type).resolve();
+ if (lambdaClass == null) {
+ return null;
+ }
+ final String methodName = lambdaClass.getMethods()[0].getName();
+ if (tryConvertPseudoLambdaToStreamApi(method, resolveStreamApiLambdaClass(expression.getProject(), streamApiMethodName))) {
+ return expression;
+ }
+ else {
+ return JavaPsiFacade.getElementFactory(expression.getProject())
+ .createExpressionFromText(expression.getText() + "::" + methodName, null);
+ }
+ }
+ return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true);
+ }
+
+ @NotNull
+ private static PsiClass resolveStreamApiLambdaClass(Project project, String streamApiMethodName) {
+ final PsiClass javaUtilStream = JavaPsiFacade.getInstance(project)
+ .findClass(StreamApiConstants.JAVA_UTIL_STREAM_STREAM, GlobalSearchScope.notScope(GlobalSearchScope.projectScope(project)));
+ LOG.assertTrue(javaUtilStream != null);
+ final PsiMethod[] methods = javaUtilStream.findMethodsByName(streamApiMethodName, false);
+ LOG.assertTrue(methods.length == 1);
+ final PsiMethod method = methods[0];
+ final PsiParameter[] parameters = method.getParameterList().getParameters();
+ LOG.assertTrue(parameters.length == 1);
+ final PsiType type = parameters[0].getType();
+ LOG.assertTrue(type instanceof PsiClassType);
+ final PsiClass resolved = ((PsiClassType)type).resolve();
+ LOG.assertTrue(resolved != null);
+ return resolved;
+ }
+
+ private static boolean tryConvertPseudoLambdaToStreamApi(final @NotNull PsiMethod method, final @NotNull PsiClass expectedReturnClass) {
+ final PsiType currentReturnType = method.getReturnType();
+ if (!(currentReturnType instanceof PsiClassType)) {
+ LOG.error("pseudo-lambda return type must be class " + currentReturnType);
+ return true;
+ }
+ final PsiClass resolvedCurrentReturnType = ((PsiClassType)currentReturnType).resolve();
+ if (expectedReturnClass.getManager().areElementsEquivalent(expectedReturnClass, resolvedCurrentReturnType)) {
+ return true;
+ }
+ final PsiCodeBlock body = method.getBody();
+ Collection returnStatements = PsiTreeUtil.findChildrenOfType(body, PsiReturnStatement.class);
+ returnStatements = ContainerUtil.filter(returnStatements, new Condition() {
+ @Override
+ public boolean value(PsiReturnStatement statement) {
+ return PsiTreeUtil.getParentOfType(statement, PsiMethod.class) == method;
+ }
+ });
+ if (returnStatements.size() != 1) {
+ return false;
+ }
+ final PsiReturnStatement returnStatement = ContainerUtil.getFirstItem(returnStatements);
+ assert returnStatement != null;
+ final PsiExpression returnValue = returnStatement.getReturnValue();
+ if (returnValue instanceof PsiNewExpression) {
+ convertNewExpression(method, (PsiNewExpression)returnValue, expectedReturnClass);
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ @NotNull
+ private static PsiExpression convertClassTypeExpression(PsiExpression expression) {
+ final PsiType type = expression.getType();
+ if (type instanceof PsiClassType) {
+ final PsiClass resolvedClass = ((PsiClassType)type).resolve();
+ if (resolvedClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(resolvedClass.getQualifiedName())) {
+ return JavaPsiFacade.getElementFactory(expression.getProject())
+ .createExpressionFromText("(" + expression.getText() + ")::isInstance", null);
+ }
+ }
+ return expression;
+ }
+
+ private static String createPipelineHeadText(PsiExpression collectionExpression, PsiExpression lambdaExpression, boolean force) {
+ if (collectionExpression instanceof PsiNewExpression) {
+ final PsiDiamondType.DiamondInferenceResult diamondResolveResult =
+ PsiDiamondTypeImpl.resolveInferredTypesNoCheck((PsiNewExpression)collectionExpression, collectionExpression);
+ if (!diamondResolveResult.getInferredTypes().isEmpty()) {
+ collectionExpression = PsiDiamondTypeUtil.expandTopLevelDiamondsInside(collectionExpression);
+ }
+ }
+ else if (collectionExpression instanceof PsiMethodCallExpression) {
+ final PsiType currentType = collectionExpression.getType();
+ if (currentType == null) {
+ return null;
+ }
+ final PsiExpression copiedExpression = (PsiExpression) collectionExpression.copy();
+ final PsiType newType = copiedExpression.getType();
+ if (!currentType.equals(newType)) {
+ collectionExpression = AddTypeArgumentsFix.addTypeArguments(copiedExpression, currentType);
+ }
+ }
+ final PsiType type = collectionExpression.getType();
+ if (type instanceof PsiClassType) {
+ final PsiClass resolved = ((PsiClassType)type).resolve();
+ LOG.assertTrue(resolved != null && resolved.getQualifiedName() != null, type);
+ return collectionExpression.getText() + ".stream()";
+ }
+ else if (type instanceof PsiArrayType) {
+ return CommonClassNames.JAVA_UTIL_ARRAYS + ".stream(" + collectionExpression.getText() + ")";
+ } else if (force) {
+ return collectionExpression.getText() + ".stream()";
+ }
+ throw new AssertionError("type: " + type + " is unexpected for expression: " + collectionExpression.getText());
+ }
+
+ private static boolean isIterableOrArray(final PsiType type) {
+ if (type instanceof PsiClassType) {
+ final PsiClass resolvedClass = ((PsiClassType)type).resolve();
+ if (resolvedClass != null) {
+ return InheritanceUtil.isInheritor(resolvedClass, CommonClassNames.JAVA_LANG_ITERABLE);
+ }
+ }
+ else if (type instanceof PsiArrayType) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ PseudoLambdaReplaceTemplate template = (PseudoLambdaReplaceTemplate)o;
+
+ if (myAcceptDefaultValue != template.myAcceptDefaultValue) return false;
+ if (!myStreamApiMethodName.equals(template.myStreamApiMethodName)) return false;
+ if (myLambdaRole != template.myLambdaRole) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = myStreamApiMethodName.hashCode();
+ result = 31 * result + myLambdaRole.hashCode();
+ result = 31 * result + (myAcceptDefaultValue ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "PseudoLambdaReplaceTemplate{" +
+ "myStreamApiMethodName='" + myStreamApiMethodName + '\'' +
+ '}';
+ }
+}
diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodInspection.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodInspection.java
index 47ff9eb5e6fa..398aabc0ff20 100644
--- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodInspection.java
+++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodInspection.java
@@ -18,16 +18,12 @@ package com.intellij.codeInspection.java18StreamApi;
import com.intellij.codeInspection.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
-import com.intellij.psi.codeStyle.JavaCodeStyleManager;
-import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
-import com.intellij.util.containers.ContainerUtil;
import org.jdom.Element;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
@@ -35,7 +31,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Collection;
-import java.util.List;
/**
* @author Dmitry Batkovich
@@ -102,10 +97,9 @@ public class StaticPseudoFunctionalStyleMethodInspection extends BaseJavaBatchLo
if (suitableHandler == null) {
return;
}
- final int lambdaIndex = validateMethodParameters(methodCallExpression, method, suitableHandler.getStreamApiMethodName() == StreamApiConstants.FAKE_FIND_MATCHED);
- if (lambdaIndex != -1) {
- holder.registerProblem(methodCallExpression.getMethodExpression(), "",
- new ReplacePseudoLambdaWithLambda(lambdaIndex, methodCallExpression, method, suitableHandler));
+ final PseudoLambdaReplaceTemplate.ValidationInfo validationInfo = suitableHandler.getTemplate().validate(methodCallExpression);
+ if (validationInfo != null) {
+ holder.registerProblem(methodCallExpression.getMethodExpression(), "Pseudo functional style code", new ReplacePseudoLambdaWithLambda(suitableHandler));
}
}
}
@@ -113,17 +107,10 @@ public class StaticPseudoFunctionalStyleMethodInspection extends BaseJavaBatchLo
}
public static class ReplacePseudoLambdaWithLambda implements LocalQuickFix {
- private final int myLambdaIndex;
- private final SmartPsiElementPointer myMethodPointer;
- private final StaticPseudoFunctionalStyleMethodOptions.PipelineElement mySuitableHandler;
+ private final StaticPseudoFunctionalStyleMethodOptions.PipelineElement myHandler;
- private ReplacePseudoLambdaWithLambda(int lambdaIndex,
- @NotNull PsiMethodCallExpression expression,
- @NotNull PsiMethod method,
- @NotNull StaticPseudoFunctionalStyleMethodOptions.PipelineElement suitableHandler) {
- myLambdaIndex = lambdaIndex;
- myMethodPointer = SmartPointerManager.getInstance(expression.getProject()).createSmartPsiElementPointer(method);
- mySuitableHandler = suitableHandler;
+ private ReplacePseudoLambdaWithLambda(StaticPseudoFunctionalStyleMethodOptions.PipelineElement handler) {
+ myHandler = handler;
}
@Nls
@@ -143,253 +130,7 @@ public class StaticPseudoFunctionalStyleMethodInspection extends BaseJavaBatchLo
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiMethodCallExpression expression = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethodCallExpression.class);
LOG.assertTrue(expression != null);
- final PsiExpression[] expressions = expression.getArgumentList().getExpressions();
- PsiExpression lambdaExpression = expressions[myLambdaIndex];
- lambdaExpression = convertClassTypeExpression(lambdaExpression);
- lambdaExpression = convertToJavaLambda(lambdaExpression, mySuitableHandler.getStreamApiMethodName());
- LOG.assertTrue(lambdaExpression != null);
-
- final PsiExpression collectionExpression = expressions[(1 + myLambdaIndex) % 2];
- final String pipelineHead = createPipelineHeadText(collectionExpression);
-
-
- final String lambdaExpressionText;
- final String elementText;
- if (!StreamApiConstants.FAKE_FIND_MATCHED.equals(mySuitableHandler.getStreamApiMethodName())) {
- elementText = mySuitableHandler.getStreamApiMethodName();
- lambdaExpressionText = lambdaExpression.getText();
- }
- else {
- elementText = expressions.length == 3
- ? String.format(StreamApiConstants.FAKE_FIND_MATCHED_WITH_DEFAULT_PATTERN, lambdaExpression.getText(), expressions[2].getText())
- : String.format(StreamApiConstants.FAKE_FIND_MATCHED_PATTERN, lambdaExpression.getText());
- lambdaExpressionText = null;
- }
- final String pipelineTail =
- StreamApiConstants.STREAM_STREAM_API_METHODS.getValue().contains(mySuitableHandler.getStreamApiMethodName())
- ? findSuitableTailMethodForCollection(myMethodPointer.getElement())
- : null;
-
- final PsiElement replaced =
- expression.replace(createPipelineExpression(pipelineHead, elementText, lambdaExpressionText, pipelineTail, project));
- JavaCodeStyleManager.getInstance(project).shortenClassReferences(replaced.getParent());
+ myHandler.getTemplate().convertToStream(expression, null, false);
}
-
- @NotNull
- private static PsiExpression convertClassTypeExpression(PsiExpression expression) {
- final PsiType type = expression.getType();
- if (type instanceof PsiClassType) {
- final PsiClass resolvedClass = ((PsiClassType)type).resolve();
- if (resolvedClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(resolvedClass.getQualifiedName())) {
- return JavaPsiFacade.getElementFactory(expression.getProject())
- .createExpressionFromText("(" + expression.getText() + ")::isInstance", null);
- }
- }
- return expression;
- }
-
- private static String createPipelineHeadText(PsiExpression collectionExpression) {
- final PsiType type = collectionExpression.getType();
- if (type instanceof PsiClassType) {
- final PsiClass resolved = ((PsiClassType)type).resolve();
- LOG.assertTrue(resolved != null && resolved.getQualifiedName() != null, type);
- return collectionExpression.getText() + ".stream()";
- }
- else if (type instanceof PsiArrayType) {
- return CommonClassNames.JAVA_UTIL_ARRAYS + ".stream(" + collectionExpression.getText() + ")";
- }
- throw new AssertionError("type: " + type + " is unexpected");
- }
-
- private static PsiExpression createPipelineExpression(String pipelineHead,
- String elementText,
- String lambdaExpression,
- String pipelineTail,
- Project project) {
- final StringBuilder sb = new StringBuilder();
- sb.append(pipelineHead).append(".").append(elementText);
- if (lambdaExpression != null) {
- sb.append("(").append(lambdaExpression).append(")");
- }
- if (pipelineTail != null) {
- sb.append(".").append(pipelineTail);
- }
- return JavaPsiFacade.getElementFactory(project).createExpressionFromText(sb.toString(), null);
- }
- }
-
- private static int validateMethodParameters(final PsiMethodCallExpression methodCallExpression, final PsiMethod method, boolean canThirdParameterExist) {
- final PsiType[] argumentTypes = methodCallExpression.getArgumentList().getExpressionTypes();
- final PsiParameter[] expectedParameters = method.getParameterList().getParameters();
-
- if (argumentTypes.length != expectedParameters.length) {
- return -1;
- }
- if (expectedParameters.length == 2 || (canThirdParameterExist && expectedParameters.length == 3)) {
- final int collectionOrArrayIndex = findCollectionOrArrayPlacement(expectedParameters);
- if (collectionOrArrayIndex == -1) {
- return -1;
- }
- return (1 + collectionOrArrayIndex) % 2;
-
- }
- return -1;
- }
-
- private static int findCollectionOrArrayPlacement(final PsiParameter[] parameters) {
- for (int i = 0, length = parameters.length; i < length; i++) {
- PsiParameter parameter = parameters[i];
- final PsiType type = parameter.getType();
- if (type instanceof PsiClassType || type instanceof PsiArrayType) {
- return i;
- }
- }
- return -1;
- }
-
- private static PsiExpression convertToJavaLambda(PsiExpression expression, String streamApiMethodName) {
- if (streamApiMethodName.equals(StreamApiConstants.FAKE_FIND_MATCHED)) {
- streamApiMethodName = StreamApiConstants.FILTER;
- }
- if (expression instanceof PsiMethodReferenceExpression) {
- return expression;
- }
- if (expression instanceof PsiLambdaExpression) {
- return expression;
- }
- if (expression instanceof PsiMethodCallExpression) {
- final PsiMethod method = ((PsiMethodCallExpression)expression).resolveMethod();
- if (method == null) {
- return null;
- }
- final PsiType type = method.getReturnType();
- if (!(type instanceof PsiClassType)) {
- return null;
- }
- final PsiClass lambdaClass = ((PsiClassType)type).resolve();
- if (lambdaClass == null) {
- return null;
- }
- final String methodName = lambdaClass.getMethods()[0].getName();
- if (tryConvertPseudoLambdaToStreamApi(method, resolveStreamApiLambdaClass(expression.getProject(), streamApiMethodName))) {
- return expression;
- }
- else {
- return JavaPsiFacade.getElementFactory(expression.getProject())
- .createExpressionFromText(expression.getText() + "::" + methodName, null);
- }
- }
- return AnonymousCanBeLambdaInspection.replacePsiElementWithLambda(expression, true);
- }
-
- @NotNull
- private static PsiClass resolveStreamApiLambdaClass(Project project, String streamApiMethodName) {
- final PsiClass javaUtilStream = JavaPsiFacade.getInstance(project)
- .findClass(StreamApiConstants.JAVA_UTIL_STREAM_STREAM, GlobalSearchScope.notScope(GlobalSearchScope.projectScope(project)));
- LOG.assertTrue(javaUtilStream != null);
- final PsiMethod[] methods = javaUtilStream.findMethodsByName(streamApiMethodName, false);
- LOG.assertTrue(methods.length == 1);
- final PsiMethod method = methods[0];
- final PsiParameter[] parameters = method.getParameterList().getParameters();
- LOG.assertTrue(parameters.length == 1);
- final PsiType type = parameters[0].getType();
- LOG.assertTrue(type instanceof PsiClassType);
- final PsiClass resolved = ((PsiClassType)type).resolve();
- LOG.assertTrue(resolved != null);
- return resolved;
- }
-
- private static boolean tryConvertPseudoLambdaToStreamApi(final @NotNull PsiMethod method, final @NotNull PsiClass expectedReturnClass) {
- final PsiType currentReturnType = method.getReturnType();
- if (!(currentReturnType instanceof PsiClassType)) {
- LOG.error("pseudo-lambda return type must be class " + currentReturnType);
- return true;
- }
- final PsiClass resolvedCurrentReturnType = ((PsiClassType)currentReturnType).resolve();
- if (expectedReturnClass.getManager().areElementsEquivalent(expectedReturnClass, resolvedCurrentReturnType)) {
- return true;
- }
- final PsiCodeBlock body = method.getBody();
- Collection returnStatements = PsiTreeUtil.findChildrenOfType(body, PsiReturnStatement.class);
- returnStatements = ContainerUtil.filter(returnStatements, new Condition() {
- @Override
- public boolean value(PsiReturnStatement statement) {
- return PsiTreeUtil.getParentOfType(statement, PsiMethod.class) == method;
- }
- });
- if (returnStatements.size() != 1) {
- return false;
- }
- final PsiReturnStatement returnStatement = ContainerUtil.getFirstItem(returnStatements);
- assert returnStatement != null;
- final PsiExpression returnValue = returnStatement.getReturnValue();
- if (returnValue instanceof PsiNewExpression) {
- convertNewExpression(method, (PsiNewExpression)returnValue, expectedReturnClass);
- return true;
- }
- else {
- return false;
- }
- }
-
- private static void convertNewExpression(PsiMethod containingMethod, PsiNewExpression newExpression, PsiClass expectedReturnClass) {
- final String expectedReturnQName = expectedReturnClass.getQualifiedName();
- LOG.assertTrue(expectedReturnQName != null);
- final PsiElementFactory factory = JavaPsiFacade.getElementFactory(newExpression.getProject());
- PsiAnonymousClass anonymousClass = PsiTreeUtil.findChildOfType(newExpression, PsiAnonymousClass.class);
- LOG.assertTrue(anonymousClass != null);
- PsiJavaCodeReferenceElement referenceElement = PsiTreeUtil.findChildOfType(anonymousClass, PsiJavaCodeReferenceElement.class);
- LOG.assertTrue(referenceElement != null);
- final PsiReferenceParameterList parameterList = PsiTreeUtil.findChildOfType(referenceElement, PsiReferenceParameterList.class);
- final PsiJavaCodeReferenceElement newCodeReferenceElement = factory.createReferenceFromText(expectedReturnClass.getQualifiedName()
- +
- (parameterList == null
- ? ""
- : parameterList.getText()), null);
- referenceElement.replace(newCodeReferenceElement);
- final List methods = ContainerUtil.filter(anonymousClass.getMethods(), new Condition() {
- @Override
- public boolean value(PsiMethod method) {
- return !"equals".equals(method.getName());
- }
- });
- LOG.assertTrue(methods.size() == 1, methods);
- final PsiMethod method = methods.get(0);
- method.setName(expectedReturnClass.getMethods()[0].getName());
- final PsiTypeElement element = containingMethod.getReturnTypeElement();
- if (element != null) {
- final PsiReferenceParameterList genericParameter = PsiTreeUtil.findChildOfType(element, PsiReferenceParameterList.class);
- element.replace(factory.createTypeElementFromText(expectedReturnQName + (genericParameter == null ? "" : genericParameter.getText()), null));
- }
- }
-
- @Nullable
- private static String findSuitableTailMethodForCollection(PsiMethod lambdaHandler) {
- final PsiType type = lambdaHandler.getReturnType();
- if (type instanceof PsiArrayType) {
- return "toArray(String[]::new)";
- }
- else if (type instanceof PsiClassType) {
- final PsiClass resolvedClass = ((PsiClassType)type).resolve();
- if (resolvedClass == null) {
- return null;
- }
- final String qName = resolvedClass.getQualifiedName();
- if (qName == null) {
- return null;
- }
- if (qName.equals(CommonClassNames.JAVA_UTIL_LIST)
- || qName.equals(CommonClassNames.JAVA_UTIL_COLLECTION)
- || qName.equals(CommonClassNames.JAVA_LANG_ITERABLE)) {
- return "collect(" + StreamApiConstants.JAVA_UTIL_STREAM_COLLECTORS + ".toList())";
- }
- else if (qName.equals(CommonClassNames.JAVA_UTIL_SET)) {
- return "collect(" + StreamApiConstants.JAVA_UTIL_STREAM_COLLECTORS + ".toSet())";
- }
- else if (qName.equals(CommonClassNames.JAVA_UTIL_ITERATOR)) {
- return "iterator()";
- }
- }
- return null;
}
}
\ No newline at end of file
diff --git a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodOptions.java b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodOptions.java
index 0fa7399ca3c8..9787c394b997 100644
--- a/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodOptions.java
+++ b/java/java-impl/src/com/intellij/codeInspection/java18StreamApi/StaticPseudoFunctionalStyleMethodOptions.java
@@ -16,11 +16,9 @@
package com.intellij.codeInspection.java18StreamApi;
import com.intellij.openapi.actionSystem.CommonDataKeys;
-import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
-import com.intellij.psi.*;
-import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import com.intellij.util.containers.ContainerUtil;
@@ -32,7 +30,6 @@ import javax.swing.*;
import javax.swing.event.ListDataListener;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
/**
@@ -43,6 +40,8 @@ public class StaticPseudoFunctionalStyleMethodOptions {
private static final String FQN_ATTR = "classFqn";
private static final String METHOD_ATTR = "method";
private static final String STREAM_API_METHOD_ATTR = "streamApiMethod";
+ private static final String LAMBDA_ROLE_ATTR = "lambdaRole";
+ private static final String ACCEPTS_DEFAULT_ATTR = "acceptsDefault";
private static final String DELETE_ATTR = "toDelete";
private final List myElements;
@@ -54,11 +53,11 @@ public class StaticPseudoFunctionalStyleMethodOptions {
private static void restoreDefault(final List elements) {
elements.clear();
final String guavaIterables = "com.google.common.collect.Iterables";
- elements.add(new PipelineElement(guavaIterables, "transform", StreamApiConstants.MAP));
- elements.add(new PipelineElement(guavaIterables, "filter", StreamApiConstants.FILTER));
- elements.add(new PipelineElement(guavaIterables, "find", StreamApiConstants.FAKE_FIND_MATCHED));
- elements.add(new PipelineElement(guavaIterables, "all", StreamApiConstants.ALL_MATCH));
- elements.add(new PipelineElement(guavaIterables, "any", StreamApiConstants.ANY_MATCH));
+ elements.add(new PipelineElement(guavaIterables, "transform", PseudoLambdaReplaceTemplate.MAP));
+ elements.add(new PipelineElement(guavaIterables, "filter", PseudoLambdaReplaceTemplate.FILTER));
+ elements.add(new PipelineElement(guavaIterables, "find", PseudoLambdaReplaceTemplate.FIND));
+ elements.add(new PipelineElement(guavaIterables, "all", PseudoLambdaReplaceTemplate.ALL_MATCH));
+ elements.add(new PipelineElement(guavaIterables, "any", PseudoLambdaReplaceTemplate.ANY_MATCH));
}
@NotNull
@@ -77,8 +76,11 @@ public class StaticPseudoFunctionalStyleMethodOptions {
final String fqn = element.getAttributeValue(FQN_ATTR);
final String method = element.getAttributeValue(METHOD_ATTR);
final String streamApiMethod = element.getAttributeValue(STREAM_API_METHOD_ATTR);
+ final PseudoLambdaReplaceTemplate.LambdaRole lambdaRole =
+ PseudoLambdaReplaceTemplate.LambdaRole.valueOf(element.getAttributeValue(LAMBDA_ROLE_ATTR));
+ final boolean acceptsDefault = Boolean.valueOf(element.getAttributeValue(ACCEPTS_DEFAULT_ATTR));
final boolean toDelete = element.getAttribute(DELETE_ATTR) != null;
- final PipelineElement pipelineElement = new PipelineElement(fqn, method, streamApiMethod);
+ final PipelineElement pipelineElement = new PipelineElement(fqn, method, new PseudoLambdaReplaceTemplate(streamApiMethod, lambdaRole, acceptsDefault));
if (toDelete) {
myElements.remove(pipelineElement);
}
@@ -107,10 +109,13 @@ public class StaticPseudoFunctionalStyleMethodOptions {
}
public Element createXmlElement(PipelineElement element) {
+ final PseudoLambdaReplaceTemplate template = element.getTemplate();
return new Element(PIPELINE_ELEMENT_NAME)
.setAttribute(FQN_ATTR, element.getHandlerClass())
.setAttribute(METHOD_ATTR, element.getMethodName())
- .setAttribute(STREAM_API_METHOD_ATTR, element.getStreamApiMethodName());
+ .setAttribute(STREAM_API_METHOD_ATTR, template.getStreamApiMethodName())
+ .setAttribute(LAMBDA_ROLE_ATTR, template.getLambdaRole().toString())
+ .setAttribute(ACCEPTS_DEFAULT_ATTR, String.valueOf(template.isAcceptDefaultValue()));
}
public JComponent createPanel() {
@@ -137,6 +142,9 @@ public class StaticPseudoFunctionalStyleMethodOptions {
if (currentProject == null) {
return;
}
+ if (DumbService.isDumb(currentProject)) {
+ return;
+ }
final AddMethodsDialog dlg = new AddMethodsDialog(currentProject, list, false);
if (dlg.showAndGet()) {
final PipelineElement newElement = dlg.getSelectedElement();
@@ -147,6 +155,7 @@ public class StaticPseudoFunctionalStyleMethodOptions {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
+ list.revalidate();
list.updateUI();
}
});
@@ -156,19 +165,28 @@ public class StaticPseudoFunctionalStyleMethodOptions {
@Override
public void run(AnActionButton button) {
myElements.remove(list.getSelectedIndex());
+ UIUtil.invokeLaterIfNeeded(new Runnable() {
+ @Override
+ public void run() {
+ list.revalidate();
+ list.updateUI();
+ }
+ });
}
}).createPanel();
}
- public static class PipelineElement {
+ public static class PipelineElement {
private final String myHandlerClass;
private final String myMethodName;
- private final String myStreamApiMethod;
+ private final PseudoLambdaReplaceTemplate myTemplate;
- public PipelineElement(@NotNull String handlerClass, @NotNull String methodName, @NotNull String streamApiMethod) {
+ public PipelineElement(@NotNull String handlerClass,
+ @NotNull String methodName,
+ @NotNull PseudoLambdaReplaceTemplate template) {
myHandlerClass = handlerClass;
myMethodName = methodName;
- myStreamApiMethod = streamApiMethod;
+ myTemplate = template;
}
public String getHandlerClass() {
@@ -179,8 +197,8 @@ public class StaticPseudoFunctionalStyleMethodOptions {
return myMethodName;
}
- public String getStreamApiMethodName() {
- return myStreamApiMethod;
+ public PseudoLambdaReplaceTemplate getTemplate() {
+ return myTemplate;
}
@Override
@@ -192,7 +210,7 @@ public class StaticPseudoFunctionalStyleMethodOptions {
if (!myHandlerClass.equals(element.myHandlerClass)) return false;
if (!myMethodName.equals(element.myMethodName)) return false;
- if (!myStreamApiMethod.equals(element.myStreamApiMethod)) return false;
+ if (!myTemplate.equals(element.myTemplate)) return false;
return true;
}
@@ -201,7 +219,7 @@ public class StaticPseudoFunctionalStyleMethodOptions {
public int hashCode() {
int result = myHandlerClass.hashCode();
result = 31 * result + myMethodName.hashCode();
- result = 31 * result + myStreamApiMethod.hashCode();
+ result = 31 * result + myTemplate.hashCode();
return result;
}
}
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/diamondResolve/test.java b/java/java-tests/testData/inspection/lambdaLibsStatic/diamondResolve/test.java
new file mode 100644
index 000000000000..2f9ec5b1b413
--- /dev/null
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/diamondResolve/test.java
@@ -0,0 +1,13 @@
+import com.google.common.collect.Iterables;
+import com.google.common.base.Predicate;
+
+import java.lang.Iterable;
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.ArrayList;
+
+class c {
+ void m() {
+ Iterable l = Iterables.filter(new ArrayList<>(), op -> false);
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/diamondResolve/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/diamondResolve/test_after.java
new file mode 100644
index 000000000000..32789bc26034
--- /dev/null
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/diamondResolve/test_after.java
@@ -0,0 +1,14 @@
+import com.google.common.collect.Iterables;
+import com.google.common.base.Predicate;
+
+import java.lang.Iterable;
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.ArrayList;
+import java.util.stream.Collectors;
+
+class c {
+ void m() {
+ Iterable l = new ArrayList().stream().filter(op -> false).collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous2/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous2/test_after.java
index 65eb750815ea..e1c89f69e2a5 100644
--- a/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous2/test_after.java
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/lambdaIsntAnonymous2/test_after.java
@@ -6,7 +6,7 @@ import java.util.Collections;
class c {
void m() {
- Collections.emptyList().stream().allMatch(getPredicate(100)::apply);
+ Collections.emptyList().stream().allMatch(getPredicate(100)::apply);
}
public Predicate getPredicate(final int param) {
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/missingTypeParameters/test.java b/java/java-tests/testData/inspection/lambdaLibsStatic/missingTypeParameters/test.java
new file mode 100644
index 000000000000..8c911af98bd0
--- /dev/null
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/missingTypeParameters/test.java
@@ -0,0 +1,13 @@
+import com.google.common.collect.Iterables;
+import com.google.common.base.Predicate;
+
+import java.lang.Iterable;
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.ArrayList;
+
+class c {
+ void m() {
+ Iterable l = Iterables.filter(Collections.emptyList(), op -> false);
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/missingTypeParameters/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/missingTypeParameters/test_after.java
new file mode 100644
index 000000000000..c5273bd3b0fc
--- /dev/null
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/missingTypeParameters/test_after.java
@@ -0,0 +1,14 @@
+import com.google.common.collect.Iterables;
+import com.google.common.base.Predicate;
+
+import java.lang.Iterable;
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.ArrayList;
+import java.util.stream.Collectors;
+
+class c {
+ void m() {
+ Iterable l = Collections.emptyList().stream().filter(op -> false).collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java
new file mode 100644
index 000000000000..cc8ef8ab6ddb
--- /dev/null
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test.java
@@ -0,0 +1,19 @@
+import com.google.common.collect.Iterables;
+import com.google.common.base.Function;
+
+import java.lang.String;
+import java.util.Collections;
+
+class c {
+ Iterable transform = Iterables.transform(Collections.emptyList(), new Function() {
+ @Override
+ public String apply(String input) {
+ Collectors c;
+ ArrayList l;
+ System.out.println(input);
+ //do something
+ int i = 1;
+ return input;
+ }
+ });
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java
new file mode 100644
index 000000000000..bf6d86ece3a8
--- /dev/null
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformLambda/test_after.java
@@ -0,0 +1,17 @@
+import com.google.common.collect.Iterables;
+import com.google.common.base.Function;
+
+import java.lang.String;
+import java.util.Collections;
+import java.util.stream.Collectors;
+
+class c {
+ Iterable transform = Collections.emptyList().stream().map(input -> {
+ Collectors c;
+ ArrayList l;
+ System.out.println(input);
+ //do something
+ int i = 1;
+ return input;
+ }).collect(Collectors.toList());
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java
new file mode 100644
index 000000000000..addd4facbcf6
--- /dev/null
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test.java
@@ -0,0 +1,17 @@
+import com.google.common.collect.Iterables;
+import com.google.common.base.Function;
+
+import java.lang.String;
+import java.util.Collections;
+
+class c {
+ void m() {
+ Iterables.transform(new ArrayList<>(), getFunction())
+ }
+
+ Function getFunction() {
+ return new Function() {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java
new file mode 100644
index 000000000000..452f94d0f295
--- /dev/null
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodCalling/test_after.java
@@ -0,0 +1,20 @@
+import com.google.common.collect.Iterables;
+import com.google.common.base.Function;
+
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.stream.Collectors;
+
+class c {
+ void m() {
+ Collections.emptyList().stream().map(input -> {
+ Collectors c;
+ ArrayList l;
+ System.out.println(input);
+ //do something
+ int i = 1;
+ return input;
+ }).collect(Collectors.toList())
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test.java
new file mode 100644
index 000000000000..73dc592b128d
--- /dev/null
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test.java
@@ -0,0 +1,21 @@
+import com.google.common.collect.Iterables;
+import com.google.common.base.Function;
+
+import java.lang.String;
+import java.util.Collections;
+
+class c {
+ void m() {
+ Iterables.transform(Collections.emptyList(), new Function () {
+ @Override
+ public String apply(String input) {
+ java.util.stream.Collectors c;
+ java.util.ArrayList l;
+ System.out.println(input);
+ //do something
+ int i = 1;
+ return input;
+ }
+ })
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java
new file mode 100644
index 000000000000..452f94d0f295
--- /dev/null
+++ b/java/java-tests/testData/inspection/lambdaLibsStatic/transformMethodRef/test_after.java
@@ -0,0 +1,20 @@
+import com.google.common.collect.Iterables;
+import com.google.common.base.Function;
+
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.stream.Collectors;
+
+class c {
+ void m() {
+ Collections.emptyList().stream().map(input -> {
+ Collectors c;
+ ArrayList l;
+ System.out.println(input);
+ //do something
+ int i = 1;
+ return input;
+ }).collect(Collectors.toList())
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java
index 1acaf1586e15..098f85b1561f 100644
--- a/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java
+++ b/java/java-tests/testSrc/com/intellij/codeInspection/StaticPseudoFunctionalStyleMethodTest.java
@@ -28,6 +28,7 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.builders.JavaModuleFixtureBuilder;
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase;
+import com.intellij.util.PathUtil;
import com.intellij.util.Processor;
import java.io.File;
@@ -91,6 +92,22 @@ public class StaticPseudoFunctionalStyleMethodTest extends JavaCodeInsightFixtur
doTest();
}
+ public void testDiamondResolve() {
+ doTest();
+ }
+
+ public void _testTransformLambda() {
+ doTest();
+ }
+
+ public void _testTransformMethodCalling() {
+ doTest();
+ }
+
+ public void testTransformMethodRef() {
+ doTest();
+ }
+
public void _testReplaceWithMethodReference() {
doTest();
}
@@ -109,7 +126,7 @@ public class StaticPseudoFunctionalStyleMethodTest extends JavaCodeInsightFixtur
}
}
}
- assertTrue(isQuickFixFound);
+ assertTrue("Quick fix isn't found", isQuickFixFound);
myFixture.checkResultByFile(getTestName(true) + "/test_after.java");
}
}
diff --git a/resources-en/src/inspectionDescriptions/StaticPseudoFunctionalStyleMethod.html b/resources-en/src/inspectionDescriptions/StaticPseudoFunctionalStyleMethod.html
index 598ae3052713..10c3f90c8cc6 100644
--- a/resources-en/src/inspectionDescriptions/StaticPseudoFunctionalStyleMethod.html
+++ b/resources-en/src/inspectionDescriptions/StaticPseudoFunctionalStyleMethod.html
@@ -1,5 +1,11 @@
Inspection detects usages of pseudo-functional code if Java Stream API is available.
+
+Consider cases when monad behaviour is emulated by static method call:
+
+
+List<String> transformedIterable = Iterables.transform(someList, someTransformFunction);
+
\ No newline at end of file
diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml
index 26bc49d9633c..06d676173454 100644
--- a/resources/src/META-INF/IdeaPlugin.xml
+++ b/resources/src/META-INF/IdeaPlugin.xml
@@ -727,7 +727,7 @@
displayName="Block marker comment"/>