From 8dec74314d937c47b427e5636d543e479ba9f6b9 Mon Sep 17 00:00:00 2001 From: Dmitry Batkovich Date: Wed, 1 Apr 2015 14:45:59 +0300 Subject: [PATCH] IDEA-138382 Added inspection to report about duplicated characters in StringTokenizer's delimiters --- .../StringTokenizerDelimiterInspection.java | 110 ++++++++++++++++++ .../afterConstructor.java | 12 ++ .../stringTokenizerDelimiter/afterMethod.java | 12 ++ .../beforeConstructor.java | 12 ++ .../beforeConstructorNoDuplicates.java | 12 ++ .../beforeMethod.java | 12 ++ ...tringTokenizerDelimiterInspectionTest.java | 47 ++++++++ .../StringTokenizerDelimiter.html | 5 + resources/src/META-INF/IdeaPlugin.xml | 5 + 9 files changed, 227 insertions(+) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/StringTokenizerDelimiterInspection.java create mode 100644 java/java-tests/testData/inspection/stringTokenizerDelimiter/afterConstructor.java create mode 100644 java/java-tests/testData/inspection/stringTokenizerDelimiter/afterMethod.java create mode 100644 java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructor.java create mode 100644 java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java create mode 100644 java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeMethod.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInspection/StringTokenizerDelimiterInspectionTest.java create mode 100644 resources-en/src/inspectionDescriptions/StringTokenizerDelimiter.html diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/StringTokenizerDelimiterInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/StringTokenizerDelimiterInspection.java new file mode 100644 index 000000000000..79a1c81f0686 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/StringTokenizerDelimiterInspection.java @@ -0,0 +1,110 @@ +/* + * 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; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import gnu.trove.THashSet; +import org.jetbrains.annotations.NotNull; + +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * @author Dmitry Batkovich + */ +public class StringTokenizerDelimiterInspection extends BaseJavaBatchLocalInspectionTool { + + private final static String NEXT_TOKEN = "nextToken"; + private final static String STRING_TOKENIZER = "java.util.StringTokenizer"; + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { + return new JavaElementVisitor() { + @Override + public void visitCallExpression(PsiCallExpression callExpression) { + final PsiExpressionList argumentList = callExpression.getArgumentList(); + final PsiMethod method = callExpression.resolveMethod(); + if (method != null && argumentList != null && (method.isConstructor() || NEXT_TOKEN.equals(method.getName()))) { + final PsiClass stringTokenizer = method.getContainingClass(); + if (stringTokenizer != null && STRING_TOKENIZER.equals(stringTokenizer.getQualifiedName())) { + final PsiExpression[] arguments = argumentList.getExpressions(); + final int argCount = arguments.length; + if (method.isConstructor()) { + if (argCount == 2 || argCount == 3) { + hasArgumentDuplicates(arguments[1], holder); + } + } + else { + if (argCount == 1) { + hasArgumentDuplicates(arguments[0], holder); + } + } + } + } + } + }; + } + + private static void hasArgumentDuplicates(PsiExpression delimiterArgument, ProblemsHolder holder) { + if (delimiterArgument instanceof PsiLiteralExpression) { + final Object value = ((PsiLiteralExpression)delimiterArgument).getValue(); + if (value instanceof String) { + String delimiters = (String)value; + final Set chars = new THashSet(); + for (char c : delimiters.toCharArray()) { + if (!chars.add(c)) { + holder.registerProblem(delimiterArgument, "Delimiters argument contains duplicated characters", new ReplaceDelimitersWithUnique(delimiterArgument)); + return; + } + } + } + } + } + + private final static class ReplaceDelimitersWithUnique extends LocalQuickFixOnPsiElement { + public ReplaceDelimitersWithUnique(@NotNull PsiElement element) { + super(element); + } + + @NotNull + @Override + public String getText() { + return getFamilyName(); + } + + @NotNull + @Override + public String getFamilyName() { + return "Replace StringTokenizer delimiters parameter with unique symbols"; + } + + @Override + public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull PsiElement startElement, @NotNull PsiElement endElement) { + final Set uniqueChars = new LinkedHashSet(); + final PsiLiteralExpression delimiterArgument = (PsiLiteralExpression)startElement; + for (char c : ((String)delimiterArgument.getValue()).toCharArray()) { + uniqueChars.add(c); + } + final String newDelimiters = StringUtil.join(uniqueChars, ""); + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); + delimiterArgument.replace(elementFactory.createExpressionFromText(StringUtil.wrapWithDoubleQuote(StringUtil.escaper(true, null).fun( + newDelimiters)), null)); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/stringTokenizerDelimiter/afterConstructor.java b/java/java-tests/testData/inspection/stringTokenizerDelimiter/afterConstructor.java new file mode 100644 index 000000000000..4fa62a645fb8 --- /dev/null +++ b/java/java-tests/testData/inspection/stringTokenizerDelimiter/afterConstructor.java @@ -0,0 +1,12 @@ +// "Replace StringTokenizer delimiters parameter with unique symbols" "true" +import java.util.StringTokenizer; + +class A { + + void m() { + + new StringTokenizer("asd", "\\\t\nqwerty") + + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/stringTokenizerDelimiter/afterMethod.java b/java/java-tests/testData/inspection/stringTokenizerDelimiter/afterMethod.java new file mode 100644 index 000000000000..da3129db4a26 --- /dev/null +++ b/java/java-tests/testData/inspection/stringTokenizerDelimiter/afterMethod.java @@ -0,0 +1,12 @@ +// "Replace StringTokenizer delimiters parameter with unique symbols" "true" +import java.util.StringTokenizer; + +class A { + + void m() { + + new StringTokenizer("asd").nextToken("\nq#r") + + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructor.java b/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructor.java new file mode 100644 index 000000000000..2fa02cd62472 --- /dev/null +++ b/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructor.java @@ -0,0 +1,12 @@ +// "Replace StringTokenizer delimiters parameter with unique symbols" "true" +import java.util.StringTokenizer; + +class A { + + void m() { + + new StringTokenizer("asd", "\\\t\t\n\nqwerty") + + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java b/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java new file mode 100644 index 000000000000..69d4839095d8 --- /dev/null +++ b/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeConstructorNoDuplicates.java @@ -0,0 +1,12 @@ +// "Replace StringTokenizer delimiters parameter with unique symbols" "false" +import java.util.StringTokenizer; + +class A { + + void m() { + + new StringTokenizer("asd", "\\\t\nqwerty!#2@$") + + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeMethod.java b/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeMethod.java new file mode 100644 index 000000000000..36bc2598a7f9 --- /dev/null +++ b/java/java-tests/testData/inspection/stringTokenizerDelimiter/beforeMethod.java @@ -0,0 +1,12 @@ +// "Replace StringTokenizer delimiters parameter with unique symbols" "true" +import java.util.StringTokenizer; + +class A { + + void m() { + + new StringTokenizer("asd").nextToken("\n\nqqq####r") + + } + +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/StringTokenizerDelimiterInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/StringTokenizerDelimiterInspectionTest.java new file mode 100644 index 000000000000..ddac3879885c --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInspection/StringTokenizerDelimiterInspectionTest.java @@ -0,0 +1,47 @@ +/* + * 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; + +import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.testFramework.IdeaTestUtil; +import org.jetbrains.annotations.NotNull; + +/** + * @author Dmitry Batkovich + */ +public class StringTokenizerDelimiterInspectionTest extends LightQuickFixParameterizedTestCase { + + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return new LocalInspectionTool[]{ + new StringTokenizerDelimiterInspection(), + }; + } + + public void test() throws Exception { doAllTests(); } + + @Override + protected String getBasePath() { + return "/inspection/stringTokenizerDelimiter"; + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } +} diff --git a/resources-en/src/inspectionDescriptions/StringTokenizerDelimiter.html b/resources-en/src/inspectionDescriptions/StringTokenizerDelimiter.html new file mode 100644 index 000000000000..3fb7a2fdefd0 --- /dev/null +++ b/resources-en/src/inspectionDescriptions/StringTokenizerDelimiter.html @@ -0,0 +1,5 @@ + + +Inspection reports StringTokenizer's delimiters which are contain duplicated characters. + + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 53da14d89b25..9a782f80660d 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -723,6 +723,11 @@ groupName="Code style issues" enabledByDefault="false" level="WARNING" implementationClass="com.intellij.codeInspection.java18StreamApi.StaticPseudoFunctionalStyleMethodInspection" displayName="Pseudo functional expression using static class"/> + com.intellij.codeInsight.daemon.quickFix.RedundantLambdaParameterTypeIntention